Skip to content

Commit

Permalink
[libunwind][AIX] Fix problem with stepping up from a leaf function wh…
Browse files Browse the repository at this point in the history
…en unwinding started in a signal handler

Summary:
The implementation of AIX unwinder gets the return address from the link area of the stack frame of a function and uses the return address to walk up functions. However, when unwinding starts from a signal handler and the function that raised the signal happens to be a leaf function and it does not have its own stack frame, the return address of the stack frame of the leaf function points to the caller of the function that calls the leaf function because the leaf function and its caller share the same stack frame. As a result, the caller of the leaf function is skipped. This patch fixes the problem by saving the LR value in sigcontext when the unwinder hits the signal handler trampoline frame and using it as the return address of the leaf function. The LR value from sigcontext is saved in the unwinding context slot for LR currently unused.

Reviewed by: stephenpeckham

Differential Revision: https://reviews.llvm.org/D158655
  • Loading branch information
xingxue-ibm committed Oct 16, 2023
1 parent b51eaeb commit 45d1511
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 34 deletions.
4 changes: 4 additions & 0 deletions libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc {
void setIP(uint32_t value) { _registers.__srr0 = value; }
uint64_t getCR() const { return _registers.__cr; }
void setCR(uint32_t value) { _registers.__cr = value; }
uint64_t getLR() const { return _registers.__lr; }
void setLR(uint32_t value) { _registers.__lr = value; }

private:
struct ppc_thread_state_t {
Expand Down Expand Up @@ -1189,6 +1191,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc64 {
void setIP(uint64_t value) { _registers.__srr0 = value; }
uint64_t getCR() const { return _registers.__cr; }
void setCR(uint64_t value) { _registers.__cr = value; }
uint64_t getLR() const { return _registers.__lr; }
void setLR(uint64_t value) { _registers.__lr = value; }

private:
struct ppc64_thread_state_t {
Expand Down
96 changes: 64 additions & 32 deletions libunwind/src/UnwindCursor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2301,27 +2301,39 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) {
functionName = ".anonymous.";
}
_LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p",
__func__, functionName,
reinterpret_cast<void *>(TBTable));
_LIBUNWIND_TRACE_UNWINDING(
"%s: Look up traceback table of func=%s at %p, pc=%p, "
"SP=%p, saves_lr=%d, stores_bc=%d",
__func__, functionName, reinterpret_cast<void *>(TBTable),
reinterpret_cast<void *>(pc),
reinterpret_cast<void *>(registers.getSP()), TBTable->tb.saves_lr,
TBTable->tb.stores_bc);
}

#if defined(__powerpc64__)
// Instruction to reload TOC register "l r2,40(r1)"
// Instruction to reload TOC register "ld r2,40(r1)"
const uint32_t loadTOCRegInst = 0xe8410028;
const int32_t unwPPCF0Index = UNW_PPC64_F0;
const int32_t unwPPCV0Index = UNW_PPC64_V0;
#else
// Instruction to reload TOC register "l r2,20(r1)"
// Instruction to reload TOC register "lwz r2,20(r1)"
const uint32_t loadTOCRegInst = 0x80410014;
const int32_t unwPPCF0Index = UNW_PPC_F0;
const int32_t unwPPCV0Index = UNW_PPC_V0;
#endif

// lastStack points to the stack frame of the next routine up.
pint_t curStack = static_cast<pint_t>(registers.getSP());
pint_t lastStack = *reinterpret_cast<pint_t *>(curStack);

if (lastStack == 0)
return UNW_STEP_END;

R newRegisters = registers;

// lastStack points to the stack frame of the next routine up.
pint_t lastStack = *(reinterpret_cast<pint_t *>(registers.getSP()));
// If backchain is not stored, use the current stack frame.
if (!TBTable->tb.stores_bc)
lastStack = curStack;

// Return address is the address after call site instruction.
pint_t returnAddress;
Expand All @@ -2331,33 +2343,41 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
reinterpret_cast<void *>(lastStack));

sigcontext *sigContext = reinterpret_cast<sigcontext *>(
reinterpret_cast<char *>(lastStack) + STKMIN);
reinterpret_cast<char *>(lastStack) + STKMINALIGN);
returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;

_LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n",
reinterpret_cast<void *>(sigContext),
reinterpret_cast<void *>(returnAddress));

bool useSTKMIN = false;
if (returnAddress < 0x10000000) {
// Try again using STKMINALIGN
// Try again using STKMIN.
sigContext = reinterpret_cast<sigcontext *>(
reinterpret_cast<char *>(lastStack) + STKMINALIGN);
reinterpret_cast<char *>(lastStack) + STKMIN);
returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
if (returnAddress < 0x10000000) {
_LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n",
reinterpret_cast<void *>(returnAddress));
_LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p from sigcontext=%p",
reinterpret_cast<void *>(returnAddress),
reinterpret_cast<void *>(sigContext));
return UNW_EBADFRAME;
} else {
_LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: "
"sigContext=%p, returnAddress=%p. "
"Seems to be a valid address\n",
reinterpret_cast<void *>(sigContext),
reinterpret_cast<void *>(returnAddress));
}
useSTKMIN = true;
}
_LIBUNWIND_TRACE_UNWINDING("Returning from a signal handler %s: "
"sigContext=%p, returnAddress=%p. "
"Seems to be a valid address",
useSTKMIN ? "STKMIN" : "STKMINALIGN",
reinterpret_cast<void *>(sigContext),
reinterpret_cast<void *>(returnAddress));

// Restore the condition register from sigcontext.
newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr);

// Save the LR in sigcontext for stepping up when the function that
// raised the signal is a leaf function. This LR has the return address
// to the caller of the leaf function.
newRegisters.setLR(sigContext->sc_jmpbuf.jmp_context.lr);
_LIBUNWIND_TRACE_UNWINDING(
"Save LR=%p from sigcontext",
reinterpret_cast<void *>(sigContext->sc_jmpbuf.jmp_context.lr));

// Restore GPRs from sigcontext.
for (int i = 0; i < 32; ++i)
newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]);
Expand All @@ -2380,13 +2400,26 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
}
} else {
// Step up a normal frame.
returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];

_LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, "
"returnAddress=%p\n",
reinterpret_cast<void *>(lastStack),
reinterpret_cast<void *>(returnAddress));
_LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n",
if (!TBTable->tb.saves_lr && registers.getLR()) {
// This case should only occur if we were called from a signal handler
// and the signal occurred in a function that doesn't save the LR.
returnAddress = registers.getLR();
_LIBUNWIND_TRACE_UNWINDING("Use saved LR=%p",
reinterpret_cast<void *>(returnAddress));
} else {
// Otherwise, use the LR value in the stack link area.
returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
}

// Reset LR in the current context.
newRegisters.setLR(NULL);

_LIBUNWIND_TRACE_UNWINDING(
"Extract info from lastStack=%p, returnAddress=%p",
reinterpret_cast<void *>(lastStack),
reinterpret_cast<void *>(returnAddress));
_LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d",
TBTable->tb.fpr_saved, TBTable->tb.gpr_saved,
TBTable->tb.saves_cr);

Expand Down Expand Up @@ -2450,7 +2483,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,

struct vec_ext *vec_ext = reinterpret_cast<struct vec_ext *>(charPtr);

_LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved);
_LIBUNWIND_TRACE_UNWINDING("vr_saved=%d", vec_ext->vr_saved);

// Restore vector register(s) if saved on the stack.
if (vec_ext->vr_saved) {
Expand Down Expand Up @@ -2480,11 +2513,11 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,

// Do we need to set the TOC register?
_LIBUNWIND_TRACE_UNWINDING(
"Current gpr2=%p\n",
"Current gpr2=%p",
reinterpret_cast<void *>(newRegisters.getRegister(2)));
if (firstInstruction == loadTOCRegInst) {
_LIBUNWIND_TRACE_UNWINDING(
"Set gpr2=%p from frame\n",
"Set gpr2=%p from frame",
reinterpret_cast<void *>(reinterpret_cast<pint_t *>(lastStack)[5]));
newRegisters.setRegister(2, reinterpret_cast<pint_t *>(lastStack)[5]);
}
Expand Down Expand Up @@ -2516,7 +2549,6 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
} else {
isSignalFrame = false;
}

return UNW_STEP_SUCCESS;
}
#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
Expand Down
14 changes: 12 additions & 2 deletions libunwind/src/UnwindRegistersSave.S
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,12 @@ LnoR2Fix:
std 0, PPC64_OFFS_CR(3)
mfxer 0
std 0, PPC64_OFFS_XER(3)
#if defined(_AIX)
// LR value saved from the register is not used, initialize it to 0.
li 0, 0
#else
mflr 0
#endif
std 0, PPC64_OFFS_LR(3)
mfctr 0
std 0, PPC64_OFFS_CTR(3)
Expand Down Expand Up @@ -565,8 +570,8 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
// is called from a different module. Save the original TOC register
// in the context if this is the case.
mflr 4
lwz 4, 0(4) // Get the instruction at the return address.
xoris 0, 4, 0x8041 // Is it reloading the TOC register "ld 2,40(1)"?
lwz 4, 0(4) // Get the instruction at the return address.
xoris 0, 4, 0x8041 // Is it reloading the TOC register "lwz 2,20(1)"?
cmplwi 0, 0x14
bne 0, LnoR2Fix // No need to fix up r2 if it is not.
lwz 2, 20(1) // Use the saved TOC register in the stack.
Expand Down Expand Up @@ -610,6 +615,11 @@ LnoR2Fix:
// save CR registers
mfcr 0
stw 0, 136(3)
#if defined(_AIX)
// LR value from the register is not used, initialize it to 0.
li 0, 0
stw 0, 144(3)
#endif
// save CTR register
mfctr 0
stw 0, 148(3)
Expand Down

0 comments on commit 45d1511

Please sign in to comment.