Skip to content

Commit 8a0c623

Browse files
authored
Fix relocation to work on the latest Rust nightly. (#116)
The trick of using a static variable initialized with an address to obtain the pre-relocated address no longer appears to work on Rust nightly, so replace it with code that reads the static start address out of the `e_start` field of the executable's ELF header. This is less "sneaky", so it should be more robust against compiler changes. And, fix the 32-bit x86 relocation code and re-enable all the tests on x86.
1 parent 0f8f179 commit 8a0c623

7 files changed

Lines changed: 140 additions & 51 deletions

File tree

src/arch/aarch64.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::arch::asm;
44
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
55
#[cfg(relocation_model = "pic")]
6-
use linux_raw_sys::elf::Elf_Dyn;
6+
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
77
#[cfg(feature = "origin-signal")]
88
use linux_raw_sys::general::__NR_rt_sigreturn;
99
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
@@ -49,9 +49,24 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
4949
asm!(
5050
".weak _DYNAMIC",
5151
".hidden _DYNAMIC",
52-
"adrp x0, _DYNAMIC",
53-
"add x0, x0, #:lo12:_DYNAMIC",
54-
out("x0") addr,
52+
"adrp {0}, _DYNAMIC",
53+
"add {0}, {0}, :lo12:_DYNAMIC",
54+
out(reg) addr
55+
);
56+
}
57+
addr
58+
}
59+
60+
/// Compute the dynamic address of `__ehdr_start`.
61+
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
62+
#[cfg(relocation_model = "pic")]
63+
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
64+
let addr: *const Elf_Ehdr;
65+
unsafe {
66+
asm!(
67+
"adrp {0}, __ehdr_start",
68+
"add {0}, {0}, :lo12:__ehdr_start",
69+
out(reg) addr
5570
);
5671
}
5772
addr

src/arch/arm.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::arch::asm;
44
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
55
#[cfg(relocation_model = "pic")]
6-
use linux_raw_sys::elf::Elf_Dyn;
6+
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
77
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
88
#[cfg(relocation_model = "pic")]
99
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
@@ -49,13 +49,36 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
4949
asm!(
5050
".weak _DYNAMIC",
5151
".hidden _DYNAMIC",
52-
"ldr r0, 2f",
53-
"1: add r0, pc, r0",
54-
"b 3f",
55-
".align 2",
56-
"2: .word _DYNAMIC-(1b+8)",
57-
"3:",
58-
out("r0") addr,
52+
"ldr {0}, 1f",
53+
"0:",
54+
"add {0}, pc, {0}",
55+
"b 2f",
56+
".p2align 2",
57+
"1:",
58+
".word _DYNAMIC-(0b+8)",
59+
"2:",
60+
out(reg) addr
61+
);
62+
}
63+
addr
64+
}
65+
66+
/// Compute the dynamic address of `__ehdr_start`.
67+
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
68+
#[cfg(relocation_model = "pic")]
69+
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
70+
let addr;
71+
unsafe {
72+
asm!(
73+
"ldr {0}, 1f",
74+
"0:",
75+
"add {0}, pc, {0}",
76+
"b 2f",
77+
".p2align 2",
78+
"1:",
79+
".word __ehdr_start-(0b+8)",
80+
"2:",
81+
out(reg) addr
5982
);
6083
}
6184
addr

src/arch/riscv64.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use core::arch::asm;
88
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
99
#[cfg(relocation_model = "pic")]
10-
use linux_raw_sys::elf::Elf_Dyn;
10+
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
1111
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
1212
#[cfg(relocation_model = "pic")]
1313
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
@@ -52,13 +52,26 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
5252
asm!(
5353
".weak _DYNAMIC",
5454
".hidden _DYNAMIC",
55-
"lla a0, _DYNAMIC",
56-
out("a0") addr,
55+
"lla {}, _DYNAMIC",
56+
out(reg) addr
5757
);
5858
}
5959
addr
6060
}
6161

62+
/// Compute the dynamic address of `__ehdr_start`.
63+
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
64+
#[cfg(relocation_model = "pic")]
65+
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
66+
let addr: *const Elf_Ehdr;
67+
unsafe {
68+
asm!(
69+
"lla {}, __ehdr_start",
70+
out(reg) addr)
71+
};
72+
addr
73+
}
74+
6275
/// Perform a single load operation, outside the Rust memory model.
6376
///
6477
/// This function conceptually casts `ptr` to a `*const *mut c_void` and loads

src/arch/x86.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::arch::asm;
44
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
55
#[cfg(relocation_model = "pic")]
6-
use linux_raw_sys::elf::Elf_Dyn;
6+
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
77
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
88
#[cfg(relocation_model = "pic")]
99
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
@@ -49,23 +49,44 @@ pub(super) unsafe extern "C" fn _start() -> ! {
4949
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
5050
#[cfg(relocation_model = "pic")]
5151
pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
52-
panic!("acting as dynamic linker not yet supported on 32-bit x86");
53-
// FIXME somehow get LLVM to accept `add dword ptr [esp], _DYNAMIC-1b` or
54-
// some other way to emit an `R_386_PC32` relocation against `_DYNAMIC`.
55-
/*
5652
let addr;
5753
unsafe {
5854
asm!(
5955
".weak _DYNAMIC",
6056
".hidden _DYNAMIC",
61-
"call 1f",
62-
"1: add dword ptr [esp], _DYNAMIC-1b",
63-
"pop eax",
64-
out("eax") addr,
65-
);
57+
"call 0f",
58+
".cfi_adjust_cfa_offset 4",
59+
"0:",
60+
"pop {0}",
61+
".cfi_adjust_cfa_offset -4",
62+
"1:",
63+
"add {0}, offset _GLOBAL_OFFSET_TABLE_+(1b-0b)",
64+
"lea {0}, [{0} + _DYNAMIC@GOTOFF]",
65+
out(reg) addr
66+
)
67+
}
68+
addr
69+
}
70+
71+
/// Compute the dynamic address of `__ehdr_start`.
72+
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
73+
#[cfg(relocation_model = "pic")]
74+
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
75+
let addr;
76+
unsafe {
77+
asm!(
78+
"call 0f",
79+
".cfi_adjust_cfa_offset 4",
80+
"0:",
81+
"pop {0}",
82+
".cfi_adjust_cfa_offset -4",
83+
"1:",
84+
"add {0}, offset _GLOBAL_OFFSET_TABLE_+(1b-0b)",
85+
"lea {0}, [{0} + __ehdr_start@GOTOFF]",
86+
out(reg) addr
87+
)
6688
}
6789
addr
68-
*/
6990
}
7091

7192
/// Perform a single load operation, outside the Rust memory model.

src/arch/x86_64.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::arch::asm;
44
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
55
#[cfg(relocation_model = "pic")]
6-
use linux_raw_sys::elf::Elf_Dyn;
6+
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
77
#[cfg(feature = "origin-signal")]
88
use linux_raw_sys::general::__NR_rt_sigreturn;
99
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
@@ -49,8 +49,22 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
4949
asm!(
5050
".weak _DYNAMIC",
5151
".hidden _DYNAMIC",
52-
"lea rax, [rip + _DYNAMIC]",
53-
out("rax") addr,
52+
"lea {}, [rip + _DYNAMIC]",
53+
out(reg) addr
54+
);
55+
}
56+
addr
57+
}
58+
59+
/// Compute the dynamic address of `__ehdr_start`.
60+
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
61+
#[cfg(relocation_model = "pic")]
62+
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
63+
let addr: *const Elf_Ehdr;
64+
unsafe {
65+
asm!(
66+
"lea {}, [rip + __ehdr_start]",
67+
out(reg) addr
5468
);
5569
}
5670
addr

src/relocate.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#![allow(clippy::cmp_null)]
2828

2929
use crate::arch::{
30-
dynamic_table_addr, relocation_load, relocation_mprotect_readonly, relocation_store,
30+
dynamic_table_addr, ehdr_addr, relocation_load, relocation_mprotect_readonly, relocation_store,
3131
};
3232
use core::ffi::c_void;
3333
use core::ptr::{null, null_mut, with_exposed_provenance};
@@ -81,12 +81,12 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
8181

8282
// There are four cases to consider here:
8383
//
84-
// 1. Static executable
84+
// 1. Static executable.
8585
// This is the trivial case. No relocations necessary.
86-
// 2. Static pie executable
86+
// 2. Static pie executable.
8787
// We need to relocate ourself. `AT_PHDR` points to our own program
8888
// headers and `AT_ENTRY` to our own entry point.
89-
// 3. Dynamic pie executable with external dynamic linker
89+
// 3. Dynamic pie executable with external dynamic linker.
9090
// We don't need to relocate ourself as the dynamic linker has already
9191
// done this. `AT_PHDR` points to our own program headers and `AT_ENTRY`
9292
// to our own entry point. `AT_BASE` contains the relocation offset of
@@ -96,19 +96,20 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
9696
// program headers and `AT_ENTRY` doesn't point to our own entry point.
9797
// `AT_BASE` contains our own relocation offset.
9898

99-
// Compute the static address of `_start`.
100-
let static_start = compute_static_start();
101-
102-
if static_start == auxv_entry {
103-
// This is case 1) or case 3). If AT_BASE doesn't exist, then we are
99+
if load_static_start() == auxv_entry {
100+
// This is case 1) or case 3). If `AT_BASE` doesn't exist, then we are
104101
// already loaded at our static address despite the lack of any dynamic
105-
// linker. As such it would be case 1). If AT_BASE exists, we have
102+
// linker. As such it would be case 1). If `AT_BASE` does exist, we have
106103
// already been relocated by the dynamic linker, which is case 3).
107104
// In either case there is no need to do any relocations.
108105
return;
109106
}
110107

111108
let offset = if auxv_base == 0 {
109+
// Obtain the static address of `_start`, which is recorded in the
110+
// entry field of the ELF header.
111+
let static_start = (*ehdr_addr()).e_entry;
112+
112113
// This is case 2) as without dynamic linker `AT_BASE` doesn't exist
113114
// and we have already excluded case 1) above.
114115
auxv_entry.wrapping_sub(static_start)
@@ -119,7 +120,7 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
119120
auxv_base
120121
};
121122

122-
// This is case 2) or 4). We need to do all R_RELATIVE relocations.
123+
// This is case 2) or 4). We need to do all `R_RELATIVE` relocations.
123124
// There should be no other kind of relocation because we are either a
124125
// static PIE binary or a dynamic linker compiled with `-Bsymbolic`.
125126

@@ -228,7 +229,7 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
228229
// entry point when AT_BASE is not zero and thus a dynamic linker is in
229230
// use. In this case the assertion would fail.
230231
if auxv_base == 0 {
231-
assert_eq!(compute_static_start(), auxv_entry);
232+
assert_eq!(load_static_start(), auxv_entry);
232233
}
233234
}
234235

@@ -288,21 +289,24 @@ unsafe fn compute_auxp(envp: *mut *mut u8) -> *const Elf_auxv_t {
288289
auxp.add(1).cast()
289290
}
290291

291-
/// Compute the static address of `_start`.
292+
/// Load the address of `_start` from static memory.
293+
///
294+
/// This function contains a static variable initialized with the address of
295+
/// the `_start` function. This requires an `R_RELATIVE` relocation, because
296+
/// it requires an absolute address exist in memory, rather than being able
297+
/// to use PC-relative addressing. This allows us to tell whether relocation
298+
/// has already been done or whether we need to do it.
292299
///
293300
/// This returns a `usize` because we don't dereference the address; we just
294301
/// use it for address computation.
295-
fn compute_static_start() -> usize {
296-
// This relies on the sneaky fact that we initialize a static variable with
297-
// the address of `_start`, and if we haven't performed relocations yet,
298-
// we'll be able to see the static address. Also, the program *just*
299-
// started so there are no other threads yet, so loading from static memory
300-
// without synchronization is fine. But we still use `asm` to do everything
301-
// we can to protect this code because the optimizer won't have any idea
302-
// what we're up to.
302+
fn load_static_start() -> usize {
303+
// Initialize a static variable with the address of `_start`.
303304
struct StaticStart(*const c_void);
304305
unsafe impl Sync for StaticStart {}
305306
static STATIC_START: StaticStart = StaticStart(crate::arch::_start as *const c_void);
307+
308+
// Use `relocation_load` to do the load because the optimizer won't have
309+
// any idea what we're up to.
306310
let static_start_addr: *const *const c_void = &STATIC_START.0;
307311
unsafe { relocation_load(static_start_addr.addr()) }
308312
}

tests/example_crates.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ fn example_crate_origin_start_crt_static_relocation_static_relocate() {
136136

137137
/// Act as dynamic linker, run `relocate`.
138138
#[test]
139-
#[cfg_attr(target_arch = "x86", ignore)] // FIXME make it work on x86
140139
fn example_crate_origin_start_dynamic_linker() {
141140
use assert_cmd::Command;
142141

0 commit comments

Comments
 (0)