Skip to content

Commit 15727b5

Browse files
authored
Implement support for the .relr relocation format (#128)
The .relr relocation format is a very compact alternative to .rel and .rela for relative relocations.
1 parent 8281707 commit 15727b5

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

src/relocate.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,19 @@ use crate::arch::{
3030
dynamic_table_addr, ehdr_addr, relocation_load, relocation_mprotect_readonly, relocation_store,
3131
};
3232
use core::ffi::c_void;
33+
use core::mem;
3334
use core::ptr::{null, null_mut, with_exposed_provenance};
3435
use linux_raw_sys::elf::*;
3536
use linux_raw_sys::general::{AT_BASE, AT_ENTRY, AT_NULL, AT_PAGESZ};
3637

38+
// The Linux UAPI headers don't define the .relr types and consts yet.
39+
#[allow(non_camel_case_types)]
40+
type Elf_Relr = usize;
41+
42+
const DT_RELRSZ: usize = 35;
43+
const DT_RELR: usize = 36;
44+
const DT_RELRENT: usize = 37;
45+
3746
/// Locate the dynamic (startup-time) relocations and perform them.
3847
///
3948
/// This is unsafer than unsafe. It's meant to be called at a time when Rust
@@ -140,6 +149,12 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
140149
let mut rel_total_size = 0;
141150
let mut rel_entry_size = 0;
142151

152+
// Relr tables contain `Elf_Relr` elements which are a compact
153+
// way of representing relative relocations.
154+
let mut relr_ptr: *const Elf_Relr = null();
155+
let mut relr_total_size = 0;
156+
let mut relr_entry_size = 0;
157+
143158
// Look through the `Elf_Dyn` entries to find the location and
144159
// size of the relocation table(s).
145160
let mut current_dyn: *const Elf_Dyn = dynv;
@@ -160,6 +175,12 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
160175
DT_RELSZ => rel_total_size = d_un.d_val as usize,
161176
DT_RELENT => rel_entry_size = d_un.d_val as usize,
162177

178+
// We found a relr table. As above, model this as
179+
// `with_exposed_provenance`.
180+
DT_RELR => relr_ptr = with_exposed_provenance(d_un.d_ptr.wrapping_add(offset)),
181+
DT_RELRSZ => relr_total_size = d_un.d_val as usize,
182+
DT_RELRENT => relr_entry_size = d_un.d_val as usize,
183+
163184
// End of the Dynamic section
164185
DT_NULL => break,
165186

@@ -209,6 +230,53 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
209230
}
210231
}
211232

233+
// Perform the relr relocations.
234+
let mut current_relr = relr_ptr;
235+
let relr_end = current_relr.byte_add(relr_total_size);
236+
let mut reloc_addr = 0;
237+
while current_relr != relr_end {
238+
let mut entry = *current_relr;
239+
current_relr = current_relr.byte_add(relr_entry_size);
240+
241+
if entry & 1 == 0 {
242+
// Entry encodes offset to relocate; calculate the location
243+
// the relocation will apply at.
244+
reloc_addr = offset + entry;
245+
246+
// Perform the relr relocation.
247+
let addend = relocation_load(reloc_addr);
248+
let reloc_value = addend.wrapping_add(offset);
249+
relocation_store(reloc_addr, reloc_value);
250+
251+
// Advance relocation location.
252+
reloc_addr += mem::size_of::<usize>();
253+
} else {
254+
// Entry encodes bitmask with locations to relocate; apply each entry.
255+
256+
let mut i = 0;
257+
loop {
258+
// Shift to next item in the bitmask.
259+
entry >>= 1;
260+
if entry == 0 {
261+
// No more entries left in the bitmask; early exit.
262+
break;
263+
}
264+
265+
if entry & 1 != 0 {
266+
// Perform the relr relocation.
267+
let addend = relocation_load(reloc_addr + i * mem::size_of::<usize>());
268+
let reloc_value = addend.wrapping_add(offset);
269+
relocation_store(reloc_addr + i * mem::size_of::<usize>(), reloc_value);
270+
}
271+
272+
i += 1;
273+
}
274+
275+
// Advance relocation location.
276+
reloc_addr += (mem::size_of::<usize>() * 8 - 1) * mem::size_of::<usize>();
277+
}
278+
}
279+
212280
// FIXME split function into two here with a hint::black_box around the
213281
// function pointer to prevent the compiler from moving code between the
214282
// functions.

tests/test_crates.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,23 @@ fn test_tls_crt_static() {
3838
);
3939
}
4040

41+
#[test]
42+
fn test_tls_crt_static_relr() {
43+
test_crate(
44+
"origin-start",
45+
&["--bin=tls", "--features=origin/experimental-relocate"],
46+
// This works with ld.bfd. ld.lld uses --pack-dyn-relocs=relr instead.
47+
// FIXME detect which linker is used and pick the appropriate flag.
48+
&[(
49+
"RUSTFLAGS",
50+
"-C target-feature=+crt-static -C link-arg=-Wl,-z,pack-relative-relocs",
51+
)],
52+
"",
53+
"",
54+
Some(200),
55+
);
56+
}
57+
4158
#[test]
4259
fn test_tls_crt_static_relocation_static() {
4360
test_crate(

0 commit comments

Comments
 (0)