From 29241e38d2d2258badad0226afded382525c1aa4 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Thu, 18 May 2017 09:12:21 +0000 Subject: [PATCH] [ELF] Support R_ARM_SBREL32 Relocation This change adds support for the R_ARM_SBREL32 relocation. The relocation is a base relative relocation that is produced by clang/llvm when -frwpi is used. The use case for the -frwpi option is position independent data for embedded systems that do not have a GOT. With -frwpi all data is accessed via an offset from a base register (usually r9), where r9 is set at run time to where the data has been loaded. The base of the data is known as the static base. The ARM ABI defines the static base as: B(S) is the addressing origin of the output segment defining the symbol S. The origin is not required to be the base address of the segment. For simplicity we choose to use the base address of the segment. The ARM procedure call standard only defines a read write variant using R_ARM_SBREL32 relocations. The read-only data is accessed via pc-relative offsets from the code, this is implemented in clang as -fropi. Fixes PR32924 Differential Revision: https://reviews.llvm.org/D33280 git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@303337 91177308-0d34-0410-b5e6-96231b3b80d8 --- ELF/InputSection.cpp | 16 ++++++++++++++++ ELF/Relocations.h | 1 + ELF/Target.cpp | 3 +++ test/ELF/arm-sbrel32.s | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 test/ELF/arm-sbrel32.s 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