Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/DeveloperDocs/ARM-Relocations.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,21 @@ movt r0, #:upper16:symbol @ R_ARM_THM_MOVT_ABS
| `R_ARM_LDRS_PC_G0` | `S + A - P` | U bit + imm4H:imm4L | [0, 255] |
| `R_ARM_LDRS_PC_G1` | `S + A - P` | U bit + imm4H:imm4L | [0, 255] |
| `R_ARM_LDRS_PC_G2` | `S + A - P` | U bit + imm4H:imm4L | [0, 255] |
| `R_ARM_THM_PC8` | `S + A - Pa` | imm8:00 (bits 7:0) | [0, 1023], 4-byte aligned |
| `R_ARM_THM_PC12` | `((S + A) \| T) - Pa` | U:imm12 | [-4095, 4095] |
| `R_ARM_LDC_PC_G0` | `S + A - P` | U bit + imm8, scaled by 4 | [0, 1023], 4-byte aligned |
| `R_ARM_LDC_PC_G1` | `S + A - P` | U bit + imm8, scaled by 4 | [0, 1023], 4-byte aligned |

`R_ARM_LDRS_PC_G0`, `R_ARM_LDRS_PC_G1`, and `R_ARM_LDRS_PC_G2` are memory-form group relocations.
They compute `X = S + A - P` and encode the residual selected by the
corresponding LDRS group mask. `U` encodes the sign and `imm4H:imm4L`
encodes the resulting 8-bit offset.
`R_ARM_LDC_PC_G0` and `R_ARM_LDC_PC_G1` are LDC/STC PC-relative group
relocations. They compute `X = S + A - P` and encode the residual selected
by the corresponding LDC group mask. `U` encodes the sign and `imm8`
encodes the residual divided by four, requiring the residual to be
4-byte aligned and no greater than 1023.

| `R_ARM_THM_PC8` | `S + A - Pa` | imm8, scaled by 4 | [0, 1023], 4-byte aligned |
| `R_ARM_THM_PC12` | `((S + A) \| T) - Pa` | U bit + imm12 | [-4095, 4095] |

`R_ARM_THM_PC8` uses `Pa = (P + 4) & ~3` — the Thumb instruction address aligned to the next 4-byte boundary — instead of `P`.

Expand Down Expand Up @@ -181,8 +189,6 @@ The table below lists every relocation that ELD's ARM backend maps to the `unsup
| 53 | `R_ARM_THM_ALU_PREL_11_0` | Thumb-2 ALU PC-relative 11:0 | |
| 55 | `R_ARM_ABS32_NOI` | 32-bit absolute, no interworking bit | |
| 56 | `R_ARM_REL32_NOI` | 32-bit PC-relative, no interworking bit | |
| 67 | `R_ARM_LDC_PC_G0` | Group reloc — LDC/STC PC-relative G0 | |
| 68 | `R_ARM_LDC_PC_G1` | Group reloc — LDC/STC PC-relative G1 | |
| 69 | `R_ARM_LDC_PC_G2` | Group reloc — LDC/STC PC-relative G2 | |
| 70 | `R_ARM_ALU_SB_G0_NC` | Group reloc — ALU section-base G0, no overflow | Implement ALU_SB_G group |
| 71 | `R_ARM_ALU_SB_G0` | Group reloc — ALU section-base G0 | Implement ALU_SB_G group |
Expand Down
4 changes: 4 additions & 0 deletions lib/Target/ARM/ARMRelocationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
DECL_ARM_APPLY_RELOC_FUNC(ldrs_pc_g0) \
DECL_ARM_APPLY_RELOC_FUNC(ldrs_pc_g1) \
DECL_ARM_APPLY_RELOC_FUNC(ldrs_pc_g2) \
DECL_ARM_APPLY_RELOC_FUNC(ldc_pc_g0) \
DECL_ARM_APPLY_RELOC_FUNC(ldc_pc_g1) \
DECL_ARM_APPLY_RELOC_FUNC(thm_pc8) \
DECL_ARM_APPLY_RELOC_FUNC(thm_pc12) \
DECL_ARM_APPLY_RELOC_FUNC(relocAddPREL1) \
Expand Down Expand Up @@ -111,6 +113,8 @@
Func(llvm::ELF::R_ARM_LDRS_PC_G0, ldrs_pc_g0, "R_ARM_LDRS_PC_G0") \
Func(llvm::ELF::R_ARM_LDRS_PC_G1, ldrs_pc_g1, "R_ARM_LDRS_PC_G1") \
Func(llvm::ELF::R_ARM_LDRS_PC_G2, ldrs_pc_g2, "R_ARM_LDRS_PC_G2") \
Func(llvm::ELF::R_ARM_LDC_PC_G0, ldc_pc_g0, "R_ARM_LDC_PC_G0") \
Func(llvm::ELF::R_ARM_LDC_PC_G1, ldc_pc_g1, "R_ARM_LDC_PC_G1") \
Func(llvm::ELF::R_ARM_THM_PC8, thm_pc8, "R_ARM_THM_PC8") \
Func(llvm::ELF::R_ARM_THM_PC12, thm_pc12, "R_ARM_THM_PC12") \
Func(llvm::ELF::R_ARM_THM_MOVW_BREL_NC, thm_movw_brel, \
Expand Down
53 changes: 53 additions & 0 deletions lib/Target/ARM/ARMRelocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,59 @@ Relocator::Result ldrs_pc_g2(Relocation &pReloc, ARMRelocator &pParent) {
return ldrs_pc_group(pReloc, pParent, /*pGroup=*/2);
}

static Relocator::Result ldc_pc_group(Relocation &pReloc, ARMRelocator &pParent,
unsigned pGroup) {
Relocator::Address S = pParent.getSymValue(&pReloc);
Relocator::Address P = pReloc.place(pParent.module());

if (getThumbBit(pParent, pReloc, /*IsJump*/ false))
helper_clear_thumb_bit(S);

uint32_t I = pReloc.target();

// LDC/STC encodes an 8-bit immediate scaled by four.
int64_t A = static_cast<int64_t>((I & 0xff) << 2);
if (!(I & 0x00800000))
A = -A;

A += pReloc.addend();

int64_t X = static_cast<int64_t>(S) + A - static_cast<int64_t>(P);

uint32_t UBit = 0x00800000;
uint64_t Magnitude = static_cast<uint64_t>(X);
if (X < 0) {
UBit = 0;
Magnitude = static_cast<uint64_t>(-X);
}

uint32_t Residual =
helper_get_rem_for_group(pGroup, static_cast<uint32_t>(Magnitude));

if (Residual >= 0x400) {
pReloc.issueUnsignedOverflow(pParent, Residual, 0, 0x3ff);
return Relocator::Overflow;
}

if (Residual & 0x3)
return Relocator::BadReloc;

I = (I & 0xff7fff00) | UBit | (Residual >> 2);

pReloc.target() = I;
return Relocator::OK;
}

// R_ARM_LDC_PC_G0: S + A - P
Relocator::Result ldc_pc_g0(Relocation &pReloc, ARMRelocator &pParent) {
return ldc_pc_group(pReloc, pParent, /*pGroup=*/0);
}

// R_ARM_LDC_PC_G1: S + A - P
Relocator::Result ldc_pc_g1(Relocation &pReloc, ARMRelocator &pParent) {
return ldc_pc_group(pReloc, pParent, /*pGroup=*/1);
}

// R_ARM_ALU_PC_Gn / R_ARM_ALU_PC_Gn_NC: ((S + A) | T) - P
// Shared worker for the whole ALU_PC group family. pGroup selects which
// group's residual to encode and pCheck selects whether encoding failure is
Expand Down
8 changes: 8 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G0/Inputs/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdio.h>

extern int get_target(void);

int main() {
printf("target = %d\n", get_target());
return 0;
}
37 changes: 37 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G0/Inputs/main.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.syntax unified
.arm
.fpu vfpv3

.text
.balign 4

.global get_target
.type get_target, %function
get_target:
load_target:
// Initial instruction has A = -8.
//
// target is 12 bytes after load_target:
//
// X = S + A - P
// = (P + 12) - 8 - P
// = 4
//
// R_ARM_LDC_PC_G0 therefore encodes an offset of +4.
//
// At runtime the ARM architectural PC is P + 8:
//
// effective address = (P + 8) + 4
// = P + 12
// = target.
vldr s0, [pc, #-8]
.reloc load_target, R_ARM_LDC_PC_G0, target

// Return the raw 32-bit value loaded into s0.
vmov r0, s0
bx lr

.balign 4
.type target, %object
target:
.word 42
17 changes: 17 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G0/R_ARM_LDC_PC_G0.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
REQUIRES: run_test
#---R_ARM_LDC_PC_G0.test---------------------- Executable------------------#
#BEGIN_COMMENT
# Test R_ARM_LDC_PC_G0 at runtime.
# Verifies that the relocation correctly encodes a VLDR PC-relative load
# and that the target value 42 can be loaded and returned at runtime.
#END_COMMENT
#START_TEST
RUN: %run_cc -nostdlib -c -o %t.main.o %p/Inputs/main.s
RUN: %readelf -r %t.main.o | %filecheck %s --check-prefix RELOCS
RUN: %run_cc -c -o %t.driver.o %p/Inputs/main.c
RUN: %run_cc -static -o %t.static.out %t.main.o %t.driver.o
RUN: %run %t.static.out | %filecheck %s

RELOCS: R_ARM_LDC_PC_G0 {{.*}} target
CHECK: target = 42
#END_TEST
8 changes: 8 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G1/Inputs/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdio.h>

extern int get_target(void);

int main() {
printf("target = %d\n", get_target());
return 0;
}
41 changes: 41 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G1/Inputs/main.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.syntax unified
.arm
.fpu vfpv3

.text
.balign 4

.global get_target
.type get_target, %function
get_target:
add_base:
// A = -8 compensates for the ARM architectural PC bias.
sub r1, pc, #8
.reloc add_base, R_ARM_ALU_PC_G0_NC, target

load_target:
// load_target is 4 bytes after add_base.
// A = -4 makes both relocations operate on the same X value.
vldr s0, [r1, #-4]
.reloc load_target, R_ARM_LDC_PC_G1, target

vmov r0, s0
bx lr

// target - add_base = 0x123c.
//
// For add_base:
// X = 0x123c - 8 = 0x1234.
//
// For load_target:
// S - P = 0x1238, and A = -4:
// X = 0x1238 - 4 = 0x1234.
//
// R_ARM_LDC_PC_G1 therefore selects residual 0x34,
// encoded as imm8 = 0x34 >> 2 = 0x0d.
.space 0x122c

.balign 4
.type target, %object
target:
.word 42
19 changes: 19 additions & 0 deletions test/ARM/RunTests/R_ARM_LDC_PC_G1/R_ARM_LDC_PC_G1.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
REQUIRES: run_test
#---R_ARM_LDC_PC_G1.test---------------------- Executable------------------#
#BEGIN_COMMENT
# Test R_ARM_LDC_PC_G1 at runtime as the final memory-access relocation
# in a two-instruction PC-relative group sequence.
# R_ARM_ALU_PC_G0_NC encodes the first group and R_ARM_LDC_PC_G1
# encodes the remaining scaled offset.
#END_COMMENT
#START_TEST
RUN: %run_cc -nostdlib -c -o %t.main.o %p/Inputs/main.s
RUN: %readelf -r %t.main.o | %filecheck %s --check-prefix RELOCS
RUN: %run_cc -c -o %t.driver.o %p/Inputs/main.c
RUN: %run_cc -static -o %t.static.out %t.main.o %t.driver.o
RUN: %run %t.static.out | %filecheck %s

RELOCS: R_ARM_ALU_PC_G0_NC {{.*}} target
RELOCS: R_ARM_LDC_PC_G1 {{.*}} target
CHECK: target = 42
#END_TEST
20 changes: 20 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G0/Inputs/main.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.syntax unified
.arm

.text
.balign 4

load_target:
// LDC with U=1 and imm8=0, so the implicit addend is A = 0.
.inst 0xed9f0100
.reloc load_target, R_ARM_LDC_PC_G0, target

bx lr

// target - load_target = 0x100.
.space 0xf8

.balign 4
.type target, %object
target:
.word 42
29 changes: 29 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G0/Inputs/negative.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.syntax unified
.arm

.text
.balign 4

.type target, %object
target:
.word 42

// Place load_target exactly 0x100 bytes after target.
.space 0xfc

load_target:
// U=1 and imm8=0 gives the implicit addend A = 0.
//
// S = target = 0x0
// P = load_target = 0x100
//
// X = S + A - P
// = 0 - 0x100
// = -0x100
//
// The relocation must clear U and encode:
// imm8 = 0x100 >> 2 = 0x40.
.inst 0xed9f0100
.reloc load_target, R_ARM_LDC_PC_G0, target

bx lr
20 changes: 20 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G0/Inputs/overflow.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.syntax unified
.arm

.text
.balign 4

load_target:
// U=1, imm8=0 gives A = 0.
.inst 0xed9f0100
.reloc load_target, R_ARM_LDC_PC_G0, target

bx lr

// target - load_target = 0x1000.
.space 0xff8

.balign 4
.type target, %object
target:
.word 42
33 changes: 33 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G0/R_ARM_LDC_PC_G0.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#---R_ARM_LDC_PC_G0.test--------------------- Executable ------------------#
#BEGIN_COMMENT
# Verify R_ARM_LDC_PC_G0 relocation is correctly applied for both positive
# and negative PC-relative offsets.
#
# Formula: S + A - P.
# The LDC/STC immediate is an 8-bit value scaled by four.
#
# Positive:
# X = +0x100
# imm8 = 0x100 >> 2 = 0x40
# U = 1
# 0xed9f0100 -> 0xed9f0140
#
# Negative:
# X = -0x100
# imm8 = 0x100 >> 2 = 0x40
# U = 0
# 0xed9f0100 -> 0xed1f0140
#END_COMMENT
#START_TEST
RUN: %clangas -c %p/Inputs/main.s -o %t.pos.o
RUN: %link %linkopts -march arm -static %t.pos.o -o %t.pos.out
RUN: %readelf -x .text %t.pos.out | %filecheck %s --check-prefix=POSITIVE

POSITIVE: 00000000 40019fed 1eff2fe1

RUN: %clangas -c %p/Inputs/negative.s -o %t.neg.o
RUN: %link %linkopts -march arm -static %t.neg.o -o %t.neg.out
RUN: %readelf -x .text %t.neg.out | %filecheck %s --check-prefix=NEGATIVE

NEGATIVE: 00000100 40011fed 1eff2fe1
#END_TEST
13 changes: 13 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G0/R_ARM_LDC_PC_G0_Overflow.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#---R_ARM_LDC_PC_G0_Overflow.test------------ Executable ------------------#
#BEGIN_COMMENT
# Verify R_ARM_LDC_PC_G0 reports overflow when the selected group
# residual cannot fit in the LDC/STC scaled 8-bit immediate.
#
# X = 0x1000 = 4096, while the maximum accepted residual is 0x3ff.
#END_COMMENT
#START_TEST
RUN: %clangas -c %p/Inputs/overflow.s -o %t.o
RUN: %not %link %linkopts -march arm -static %t.o -o %t.out 2>&1 | %filecheck %s

CHECK: R_ARM_LDC_PC_G0 out of range: 4096 is not in [0, 1023]
#END_TEST
19 changes: 19 additions & 0 deletions test/ARM/standalone/R_ARM_LDC_PC_G1/Inputs/main.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.syntax unified
.arm

.text
.balign 4

load_target:
// U=1 and imm8=0 gives A = 0.
.inst 0xed9f0100
.reloc load_target, R_ARM_LDC_PC_G1, target

bx lr

// target - load_target = 0x1234.
.space 0x122c

.type target, %object
target:
.word 42
Loading
Loading