Skip to content
Merged
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
23 changes: 19 additions & 4 deletions src/arch/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Dyn;
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
#[cfg(feature = "origin-signal")]
use linux_raw_sys::general::__NR_rt_sigreturn;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
Expand Down Expand Up @@ -49,9 +49,24 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"adrp x0, _DYNAMIC",
"add x0, x0, #:lo12:_DYNAMIC",
out("x0") addr,
"adrp {0}, _DYNAMIC",
"add {0}, {0}, :lo12:_DYNAMIC",
out(reg) addr
);
}
addr
}

/// Compute the dynamic address of `__ehdr_start`.
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr: *const Elf_Ehdr;
unsafe {
asm!(
"adrp {0}, __ehdr_start",
"add {0}, {0}, :lo12:__ehdr_start",
out(reg) addr
);
}
addr
Expand Down
39 changes: 31 additions & 8 deletions src/arch/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Dyn;
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
Expand Down Expand Up @@ -49,13 +49,36 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"ldr r0, 2f",
"1: add r0, pc, r0",
"b 3f",
".align 2",
"2: .word _DYNAMIC-(1b+8)",
"3:",
out("r0") addr,
"ldr {0}, 1f",
"0:",
"add {0}, pc, {0}",
"b 2f",
".p2align 2",
"1:",
".word _DYNAMIC-(0b+8)",
"2:",
out(reg) addr
);
}
addr
}

/// Compute the dynamic address of `__ehdr_start`.
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr;
unsafe {
asm!(
"ldr {0}, 1f",
"0:",
"add {0}, pc, {0}",
"b 2f",
".p2align 2",
"1:",
".word __ehdr_start-(0b+8)",
"2:",
out(reg) addr
);
}
addr
Expand Down
19 changes: 16 additions & 3 deletions src/arch/riscv64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Dyn;
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
Expand Down Expand Up @@ -52,13 +52,26 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"lla a0, _DYNAMIC",
out("a0") addr,
"lla {}, _DYNAMIC",
out(reg) addr
);
}
addr
}

/// Compute the dynamic address of `__ehdr_start`.
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr: *const Elf_Ehdr;
unsafe {
asm!(
"lla {}, __ehdr_start",
out(reg) addr)
};
addr
}

/// Perform a single load operation, outside the Rust memory model.
///
/// This function conceptually casts `ptr` to a `*const *mut c_void` and loads
Expand Down
43 changes: 32 additions & 11 deletions src/arch/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Dyn;
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
Expand Down Expand Up @@ -49,23 +49,44 @@ pub(super) unsafe extern "C" fn _start() -> ! {
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
panic!("acting as dynamic linker not yet supported on 32-bit x86");
// FIXME somehow get LLVM to accept `add dword ptr [esp], _DYNAMIC-1b` or
// some other way to emit an `R_386_PC32` relocation against `_DYNAMIC`.
/*
let addr;
unsafe {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"call 1f",
"1: add dword ptr [esp], _DYNAMIC-1b",
"pop eax",
out("eax") addr,
);
"call 0f",
".cfi_adjust_cfa_offset 4",
"0:",
"pop {0}",
".cfi_adjust_cfa_offset -4",
"1:",
"add {0}, offset _GLOBAL_OFFSET_TABLE_+(1b-0b)",
"lea {0}, [{0} + _DYNAMIC@GOTOFF]",
out(reg) addr
)
}
addr
}

/// Compute the dynamic address of `__ehdr_start`.
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr;
unsafe {
asm!(
"call 0f",
".cfi_adjust_cfa_offset 4",
"0:",
"pop {0}",
".cfi_adjust_cfa_offset -4",
"1:",
"add {0}, offset _GLOBAL_OFFSET_TABLE_+(1b-0b)",
"lea {0}, [{0} + __ehdr_start@GOTOFF]",
out(reg) addr
)
}
addr
*/
}

/// Perform a single load operation, outside the Rust memory model.
Expand Down
20 changes: 17 additions & 3 deletions src/arch/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Dyn;
use linux_raw_sys::elf::{Elf_Dyn, Elf_Ehdr};
#[cfg(feature = "origin-signal")]
use linux_raw_sys::general::__NR_rt_sigreturn;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
Expand Down Expand Up @@ -49,8 +49,22 @@ pub(super) fn dynamic_table_addr() -> *const Elf_Dyn {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"lea rax, [rip + _DYNAMIC]",
out("rax") addr,
"lea {}, [rip + _DYNAMIC]",
out(reg) addr
);
}
addr
}

/// Compute the dynamic address of `__ehdr_start`.
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr: *const Elf_Ehdr;
unsafe {
asm!(
"lea {}, [rip + __ehdr_start]",
out(reg) addr
);
}
addr
Expand Down
46 changes: 25 additions & 21 deletions src/relocate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#![allow(clippy::cmp_null)]

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

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

// Compute the static address of `_start`.
let static_start = compute_static_start();

if static_start == auxv_entry {
// This is case 1) or case 3). If AT_BASE doesn't exist, then we are
if load_static_start() == auxv_entry {
// This is case 1) or case 3). If `AT_BASE` doesn't exist, then we are
// already loaded at our static address despite the lack of any dynamic
// linker. As such it would be case 1). If AT_BASE exists, we have
// linker. As such it would be case 1). If `AT_BASE` does exist, we have
// already been relocated by the dynamic linker, which is case 3).
// In either case there is no need to do any relocations.
return;
}

let offset = if auxv_base == 0 {
// Obtain the static address of `_start`, which is recorded in the
// entry field of the ELF header.
let static_start = (*ehdr_addr()).e_entry;

// This is case 2) as without dynamic linker `AT_BASE` doesn't exist
// and we have already excluded case 1) above.
auxv_entry.wrapping_sub(static_start)
Expand All @@ -119,7 +120,7 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
auxv_base
};

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

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

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

/// Compute the static address of `_start`.
/// Load the address of `_start` from static memory.
///
/// This function contains a static variable initialized with the address of
/// the `_start` function. This requires an `R_RELATIVE` relocation, because
/// it requires an absolute address exist in memory, rather than being able
/// to use PC-relative addressing. This allows us to tell whether relocation
/// has already been done or whether we need to do it.
///
/// This returns a `usize` because we don't dereference the address; we just
/// use it for address computation.
fn compute_static_start() -> usize {
// This relies on the sneaky fact that we initialize a static variable with
// the address of `_start`, and if we haven't performed relocations yet,
// we'll be able to see the static address. Also, the program *just*
// started so there are no other threads yet, so loading from static memory
// without synchronization is fine. But we still use `asm` to do everything
// we can to protect this code because the optimizer won't have any idea
// what we're up to.
fn load_static_start() -> usize {
// Initialize a static variable with the address of `_start`.
struct StaticStart(*const c_void);
unsafe impl Sync for StaticStart {}
static STATIC_START: StaticStart = StaticStart(crate::arch::_start as *const c_void);

// Use `relocation_load` to do the load because the optimizer won't have
// any idea what we're up to.
let static_start_addr: *const *const c_void = &STATIC_START.0;
unsafe { relocation_load(static_start_addr.addr()) }
}
1 change: 0 additions & 1 deletion tests/example_crates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ fn example_crate_origin_start_crt_static_relocation_static_relocate() {

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

Expand Down