|
1 | 1 | use core::ops::Range; |
| 2 | +use core::{ptr, slice}; |
2 | 3 |
|
| 4 | +use elf::abi; |
| 5 | +use elf::file::Elf64_Ehdr; |
| 6 | +use elf::segment::Elf64_Phdr; |
3 | 7 | use hermit_entry::boot_info::TlsInfo; |
4 | 8 |
|
5 | 9 | pub fn executable_ptr_range() -> Range<*mut ()> { |
@@ -57,5 +61,109 @@ fn executable_end() -> *mut () { |
57 | 61 | } |
58 | 62 |
|
59 | 63 | 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 |
61 | 169 | } |
0 commit comments