Skip to content

Commit b5a34d5

Browse files
committed
feat(env): implement tls_info in terms of linker symbols
1 parent e1b0217 commit b5a34d5

3 files changed

Lines changed: 117 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ build-time = "0.1.3"
295295
crossbeam-utils = { version = "0.8", default-features = false }
296296
delegate = "0.13"
297297
document-features = { version = "0.2", optional = true }
298+
elf = { version = "0.8", default-features = false }
298299
embedded-io = { version = "0.7", features = ["alloc"] }
299300
endian-num = { version = "0.2", optional = true, features = ["linux-types"] }
300301
enum_dispatch = "0.3"

src/env/executable.rs

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use core::ops::Range;
2+
use core::{ptr, slice};
23

4+
use elf::abi;
5+
use elf::file::Elf64_Ehdr;
6+
use elf::segment::Elf64_Phdr;
37
use hermit_entry::boot_info::TlsInfo;
48

59
pub fn executable_ptr_range() -> Range<*mut ()> {
@@ -57,5 +61,109 @@ fn executable_end() -> *mut () {
5761
}
5862

5963
pub fn tls_info() -> Option<TlsInfo> {
60-
super::boot_info().load_info.tls_info
64+
let ehdr = ehdr();
65+
ehdr.sanity_check_ident();
66+
67+
let phdrs = unsafe { ehdr.phdrs() };
68+
let tls_phdr = phdrs.iter().find(|phdr| phdr.p_type == abi::PT_TLS)?;
69+
let executable_start = executable_ptr_range().start.expose_provenance() as u64;
70+
71+
let tls_info = TlsInfo {
72+
start: executable_start + tls_phdr.p_vaddr,
73+
filesz: tls_phdr.p_filesz,
74+
memsz: tls_phdr.p_memsz,
75+
align: tls_phdr.p_align,
76+
};
77+
78+
assert!(tls_info_eq(
79+
&tls_info,
80+
&super::boot_info().load_info.tls_info.unwrap()
81+
));
82+
83+
Some(tls_info)
84+
}
85+
86+
fn ehdr() -> &'static Elf64_Ehdr {
87+
unsafe extern "C" {
88+
/// ELF file header.
89+
///
90+
/// Apart from changelogs, it is not documented, but defined by:
91+
///
92+
/// - ld: [binutils-gdb@`62655c7`]
93+
/// - gold: [binutils-gdb@`eabc84f`]
94+
/// - lld: [llvm/llvm-project@`4f7a5c3`]
95+
/// - mold: [rui314/mold@`ccc7f83`]
96+
/// - Wild: [wild-linker/wild@`fb7da78`]
97+
///
98+
/// [binutils-gdb@`62655c7`]: https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=62655c7b8bfc33e6c12694f439ff8f7e8da3005a
99+
/// [binutils-gdb@`eabc84f`]: https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=eabc84f4848311a68f65df04e428c8b53a92f1c0
100+
/// [llvm/llvm-project@`4f7a5c3`]: https://github.com/llvm/llvm-project/commit/4f7a5c3bb429c945ff4e456478b5532f828d1143
101+
/// [rui314/mold@`ccc7f83`]: https://github.com/rui314/mold/commit/ccc7f83cde954048424fa488464be41f210e60d1
102+
/// [wild-linker/wild@`fb7da78`]: https://github.com/wild-linker/wild/commit/fb7da7841ad9e64e2cd1128a62d23d488dae921d
103+
static __ehdr_start: Elf64_Ehdr;
104+
}
105+
106+
unsafe { &__ehdr_start }
107+
}
108+
109+
#[expect(non_camel_case_types)]
110+
trait Elf64_EhdrExt {
111+
fn sanity_check_ident(&self);
112+
113+
unsafe fn phdrs(&self) -> &[Elf64_Phdr];
114+
}
115+
116+
impl Elf64_EhdrExt for Elf64_Ehdr {
117+
fn sanity_check_ident(&self) {
118+
let ident = &self.e_ident;
119+
120+
let magic = &ident[..abi::EI_CLASS];
121+
assert_eq!(magic, abi::ELFMAGIC);
122+
123+
let version = ident[abi::EI_VERSION];
124+
assert_eq!(version, abi::EV_CURRENT);
125+
126+
let class = ident[abi::EI_CLASS];
127+
assert_eq!(class, abi::ELFCLASS64);
128+
129+
/// 2's complement values, with native endianness.
130+
pub const ELFDATA2NATIVE: u8 = if cfg!(target_endian = "little") {
131+
abi::ELFDATA2LSB
132+
} else if cfg!(target_endian = "big") {
133+
abi::ELFDATA2MSB
134+
} else {
135+
unreachable!()
136+
};
137+
138+
let data = ident[abi::EI_DATA];
139+
assert_eq!(data, ELFDATA2NATIVE);
140+
141+
/// Stand-alone (embedded) ABI
142+
pub const ELFOSABI_STANDALONE: u8 = 255;
143+
144+
let osabi = ident[abi::EI_OSABI];
145+
// For some reason `x86_64-unknown-none` uses `ELFOSABI_GNU`.
146+
// We need to allow this for `no_std` applications such as our integration tests.
147+
assert!(osabi == ELFOSABI_STANDALONE || osabi == abi::ELFOSABI_GNU);
148+
149+
let abiversion = ident[abi::EI_ABIVERSION];
150+
assert_eq!(abiversion, 0);
151+
}
152+
153+
unsafe fn phdrs(&self) -> &[Elf64_Phdr] {
154+
let ptr = unsafe {
155+
ptr::from_ref(self)
156+
.byte_add(self.e_phoff as usize)
157+
.cast::<Elf64_Phdr>()
158+
};
159+
let len = self.e_phnum as usize;
160+
unsafe { slice::from_raw_parts(ptr, len) }
161+
}
162+
}
163+
164+
fn tls_info_eq(this: &TlsInfo, other: &TlsInfo) -> bool {
165+
this.start == other.start
166+
&& this.filesz == other.filesz
167+
&& this.memsz == other.memsz
168+
&& this.align == other.align
61169
}

0 commit comments

Comments
 (0)