diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp index 87896ec96..def8b2a03 100644 --- a/ELF/InputSection.cpp +++ b/ELF/InputSection.cpp @@ -390,6 +390,20 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A, } } +// ARM SBREL relocations are of the form S + A - B where B is the static base +// The ARM ABI defines base to be "addressing origin of the output segment +// defining the symbol S". We defined the "addressing origin"/static base to be +// the base of the PT_LOAD segment containing the Body. +// The procedure call standard only defines a Read Write Position Independent +// RWPI variant so in practice we should expect the static base to be the base +// of the RW segment. +static uint64_t getARMStaticBase(const SymbolBody &Body) { + OutputSection *OS = Body.getOutputSection(); + if (!OS || !OS->FirstInPtLoad) + fatal("SBREL relocation to " + Body.getName() + " without static base\n"); + return OS->FirstInPtLoad->Addr; +} + template static typename ELFT::uint getRelocTargetVA(uint32_t Type, int64_t A, typename ELFT::uint P, @@ -398,6 +412,8 @@ getRelocTargetVA(uint32_t Type, int64_t A, typename ELFT::uint P, case R_ABS: case R_RELAX_GOT_PC_NOPIC: return Body.getVA(A); + case R_ARM_SBREL: + return Body.getVA(A) - getARMStaticBase(Body); case R_GOT: case R_RELAX_TLS_GD_TO_IE_ABS: return Body.getGotVA() + A; diff --git a/ELF/Relocations.h b/ELF/Relocations.h index f3512e0a8..206f0d942 100644 --- a/ELF/Relocations.h +++ b/ELF/Relocations.h @@ -27,6 +27,7 @@ class OutputSection; // doesn't have to know about architecture-specific details. enum RelExpr { R_ABS, + R_ARM_SBREL, R_GOT, R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, diff --git a/ELF/Target.cpp b/ELF/Target.cpp index 781d7fe3b..cf7d912ad 100644 --- a/ELF/Target.cpp +++ b/ELF/Target.cpp @@ -1693,6 +1693,8 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S, case R_ARM_TLS_IE32: // GOT(S) + A - P return R_GOT_PC; + case R_ARM_SBREL32: + return R_ARM_SBREL; case R_ARM_TARGET1: return Config->Target1Rel ? R_PC : R_ABS; case R_ARM_TARGET2: @@ -1832,6 +1834,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, case R_ARM_GOT_PREL: case R_ARM_REL32: case R_ARM_RELATIVE: + case R_ARM_SBREL32: case R_ARM_TARGET1: case R_ARM_TARGET2: case R_ARM_TLS_GD32: diff --git a/test/ELF/arm-sbrel32.s b/test/ELF/arm-sbrel32.s new file mode 100644 index 000000000..7f1271719 --- /dev/null +++ b/test/ELF/arm-sbrel32.s @@ -0,0 +1,39 @@ +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t +// RUN: ld.lld %t -o %t2 2>&1 +// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s +// REQUIRES: arm + +// Test the R_ARM_SBREL32 relocation which calculates the offset of the Symbol +// from the static base. We define the static base to be the address of the +// segment containing the symbol + .text + .syntax unified + + .globl _start + .p2align 2 + .type _start,%function +_start: + .fnstart + bx lr + + .long foo(sbrel) + .long foo2(sbrel) + .long foo3(sbrel) + .long foo4(sbrel) +// RW segment starts here + .data + .p2align 4 +foo: .word 10 +foo2: .word 20 + + .bss +foo3: .space 4 +foo4: .space 4 + +// CHECK: Disassembly of section .text: +// CHECK-NEXT: _start: +// CHECK-NEXT: 11000: 1e ff 2f e1 bx lr +// CHECK: 11004: 00 00 00 00 .word 0x00000000 +// CHECK-NEXT: 11008: 04 00 00 00 .word 0x00000004 +// CHECK-NEXT: 1100c: 08 00 00 00 .word 0x00000008 +// CHECK-NEXT: 11010: 0c 00 00 00 .word 0x0000000c