Skip to content

Commit 9d1bb8e

Browse files
Kenomaleadt
authored andcommitted
Add support for unwinding during prologue/epilogue
(cherry picked from commit 5393efb)
1 parent df90420 commit 9d1bb8e

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

libunwind/src/CompactUnwinder.hpp

+156
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,50 @@ int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
311311
uint32_t savedRegistersLocations =
312312
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
313313

314+
// If we have not stored EBP yet
315+
if (functionStart == registers.getIP()) {
316+
uint64_t rsp = registers.getSP();
317+
// old esp is ebp less return address
318+
registers.setSP(rsp+8);
319+
// pop return address into eip
320+
registers.setIP(addressSpace.get64(rsp));
321+
322+
return UNW_STEP_SUCCESS;
323+
} else if (functionStart + 1 == registers.getIP()) {
324+
uint64_t rsp = registers.getSP();
325+
// old esp is ebp less return address
326+
registers.setSP(rsp + 16);
327+
// pop return address into eip
328+
registers.setIP(addressSpace.get64(rsp + 8));
329+
330+
return UNW_STEP_SUCCESS;
331+
}
332+
333+
// If we're about to return, we've already popped the base pointer
334+
uint8_t b = addressSpace.get8(registers.getIP());
335+
336+
// This is a hack to detect VZEROUPPER but in between popq rbp and ret
337+
// It's not pretty but it works
338+
if (b == 0xC5) {
339+
if ((b = addressSpace.get8(registers.getIP() + 1)) == 0xF8 &&
340+
(b = addressSpace.get8(registers.getIP() + 2)) == 0x77)
341+
b = addressSpace.get8(registers.getIP() + 3);
342+
else
343+
goto skip_ret;
344+
}
345+
346+
if (b == 0xC3 || b == 0xCB || b == 0xC2 || b == 0xCA) {
347+
uint64_t rbp = registers.getSP();
348+
// old esp is ebp less return address
349+
registers.setSP(rbp + 16);
350+
// pop return address into eip
351+
registers.setIP(addressSpace.get64(rbp + 8));
352+
353+
return UNW_STEP_SUCCESS;
354+
}
355+
356+
skip_ret:
357+
314358
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
315359
for (int i = 0; i < 5; ++i) {
316360
switch (savedRegistersLocations & 0x7) {
@@ -431,6 +475,118 @@ int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
431475
}
432476
}
433477
}
478+
479+
// Note that the order of these registers is so that
480+
// registersSaved[0] is the one that will be pushed onto the stack last.
481+
// Thus, if we want to walk this from the top, we need to go in reverse.
482+
assert(regCount <= 6);
483+
484+
// check whether we are still in the prologue
485+
uint64_t curAddr = functionStart;
486+
if (regCount > 0) {
487+
for (int8_t i = (int8_t)(regCount) - 1; i >= 0; --i) {
488+
if (registers.getIP() == curAddr) {
489+
// None of the registers have been modified yet, so we don't need to reload them
490+
framelessUnwind(addressSpace, registers.getSP() + 8 * (regCount - (uint64_t)(i + 1)), registers);
491+
return UNW_STEP_SUCCESS;
492+
} else {
493+
assert(curAddr < registers.getIP());
494+
}
495+
496+
497+
// pushq %rbp and pushq %rbx is 1 byte. Everything else 2
498+
if ((UNWIND_X86_64_REG_RBP == registersSaved[i]) ||
499+
(UNWIND_X86_64_REG_RBX == registersSaved[i]))
500+
curAddr += 1;
501+
else
502+
curAddr += 2;
503+
}
504+
}
505+
if (registers.getIP() == curAddr) {
506+
// None of the registers have been modified yet, so we don't need to reload them
507+
framelessUnwind(addressSpace, registers.getSP() + 8*regCount, registers);
508+
return UNW_STEP_SUCCESS;
509+
} else {
510+
assert(curAddr < registers.getIP());
511+
}
512+
513+
514+
// And now for the epilogue
515+
{
516+
uint8_t i = 0;
517+
uint64_t p = registers.getIP();
518+
uint8_t b = 0;
519+
520+
while (true) {
521+
b = addressSpace.get8(p++);
522+
// This is a hack to detect VZEROUPPER but in between the popq's and ret
523+
// It's not pretty but it works
524+
if (b == 0xC5) {
525+
if ((b = addressSpace.get8(p++)) == 0xF8 && (b = addressSpace.get8(p++)) == 0x77)
526+
b = addressSpace.get8(p++);
527+
else
528+
break;
529+
}
530+
// popq %rbx popq %rbp
531+
if (b == 0x5B || b == 0x5D) {
532+
i++;
533+
} else if (b == 0x41) {
534+
b = addressSpace.get8(p++);
535+
if (b == 0x5C || b == 0x5D || b == 0x5E || b == 0x5F)
536+
i++;
537+
else
538+
break;
539+
} else if (b == 0xC3 || b == 0xCB || b == 0xC2 || b == 0xCA) {
540+
// i pop's haven't happened yet
541+
uint64_t savedRegisters = registers.getSP() + 8 * i;
542+
if (regCount > 0) {
543+
for (int8_t j = (int8_t)(regCount) - 1; j >= (int8_t)(regCount) - i; --j) {
544+
uint64_t addr = savedRegisters - 8 * (regCount - (uint64_t)(j));
545+
switch (registersSaved[j]) {
546+
case UNWIND_X86_64_REG_RBX:
547+
registers.setRBX(addressSpace.get64(addr));
548+
break;
549+
case UNWIND_X86_64_REG_R12:
550+
registers.setR12(addressSpace.get64(addr));
551+
break;
552+
case UNWIND_X86_64_REG_R13:
553+
registers.setR13(addressSpace.get64(addr));
554+
break;
555+
case UNWIND_X86_64_REG_R14:
556+
registers.setR14(addressSpace.get64(addr));
557+
break;
558+
case UNWIND_X86_64_REG_R15:
559+
registers.setR15(addressSpace.get64(addr));
560+
break;
561+
case UNWIND_X86_64_REG_RBP:
562+
registers.setRBP(addressSpace.get64(addr));
563+
break;
564+
default:
565+
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
566+
"function starting at 0x%llX",
567+
encoding, functionStart);
568+
_LIBUNWIND_ABORT("invalid compact unwind encoding");
569+
}
570+
}
571+
}
572+
framelessUnwind(addressSpace, savedRegisters, registers);
573+
return UNW_STEP_SUCCESS;
574+
} else {
575+
break;
576+
}
577+
}
578+
}
579+
580+
/*
581+
0x10fe2733a: 5b popq %rbx
582+
0x10fe2733b: 41 5c popq %r12
583+
0x10fe2733d: 41 5d popq %r13
584+
0x10fe2733f: 41 5e popq %r14
585+
0x10fe27341: 41 5f popq %r15
586+
0x10fe27343: 5d popq %rbp
587+
*/
588+
589+
434590
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
435591
for (uint32_t i = 0; i < regCount; ++i) {
436592
switch (registersSaved[i]) {

0 commit comments

Comments
 (0)