Skip to content

Commit

Permalink
[PPC64][libunwind] Fix r2 not properly restored
Browse files Browse the repository at this point in the history
This change makes each unwind step inspect the instruction at the
return address and, if needed, read r2 from its saved location and
modify the context appropriately.

The unwind logic is able to handle both ELFv1 and ELFv2 stacks.

Reported by Bug 41050

Patch by Leandro Lupori!

Differential Revision: https://reviews.llvm.org/D59694

llvm-svn: 360861
  • Loading branch information
mstorsjo committed May 16, 2019
1 parent ec895f1 commit 44266b9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
25 changes: 25 additions & 0 deletions libunwind/src/DwarfInstructions.hpp
Expand Up @@ -232,6 +232,31 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
}
#endif

#if defined(_LIBUNWIND_TARGET_PPC64)
#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1)
#define PPC64_ELFV1_R2_OFFSET 40
#define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1)
#define PPC64_ELFV2_R2_OFFSET 24
// If the instruction at return address is a TOC (r2) restore,
// then r2 was saved and needs to be restored.
// ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24,
// while in ELFv1 ABI it is saved at SP + 40.
if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) {
pint_t sp = newRegisters.getRegister(UNW_REG_SP);
pint_t r2 = 0;
switch (addressSpace.get32(returnAddress)) {
case PPC64_ELFV1_R2_LOAD_INST_ENCODING:
r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET);
break;
case PPC64_ELFV2_R2_LOAD_INST_ENCODING:
r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET);
break;
}
if (r2)
newRegisters.setRegister(UNW_PPC64_R2, r2);
}
#endif

// Return address is address after call site instruction, so setting IP to
// that does simualates a return.
newRegisters.setIP(returnAddress);
Expand Down
18 changes: 17 additions & 1 deletion libunwind/src/assembly.h
Expand Up @@ -36,6 +36,20 @@
#define SEPARATOR ;
#endif

#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1)
#define PPC64_OPD1 .section .opd,"aw",@progbits SEPARATOR
#define PPC64_OPD2 SEPARATOR \
.p2align 3 SEPARATOR \
.quad .Lfunc_begin0 SEPARATOR \
.quad .TOC.@tocbase SEPARATOR \
.quad 0 SEPARATOR \
.text SEPARATOR \
.Lfunc_begin0:
#else
#define PPC64_OPD1
#define PPC64_OPD2
#endif

#define GLUE2(a, b) a ## b
#define GLUE(a, b) GLUE2(a, b)
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
Expand Down Expand Up @@ -123,7 +137,9 @@
.globl SYMBOL_NAME(name) SEPARATOR \
HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR \
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
SYMBOL_NAME(name):
PPC64_OPD1 \
SYMBOL_NAME(name): \
PPC64_OPD2

#if defined(__arm__)
#if !defined(__ARM_ARCH)
Expand Down
3 changes: 3 additions & 0 deletions libunwind/test/lit.cfg
Expand Up @@ -23,6 +23,9 @@ config.suffixes = ['.cpp', '.s']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)

# needed to test libunwind with code that throws exceptions
config.enable_exceptions = True

# Infer the libcxx_test_source_root for configuration import.
# If libcxx_source_root isn't specified in the config, assume that the libcxx
# and libunwind source directories are sibling directories.
Expand Down

0 comments on commit 44266b9

Please sign in to comment.