Skip to content

Commit 1b5a1ae

Browse files
committed
Implement the DT_GNU_HASH mechanism for vDSO parsing.
Linux recently [removed] the `DT_HASH` section from the aarch64 vDSO. To continue to be able to read vDSOs, implement the `DT_GNU_HASH` section, following the logic in [this patch]. This also updates to linux-raw-sys 0.7 and updates the io_uring bindings for the API changes. [removed]: torvalds/linux@48f6430 [this patch]: https://lkml.org/lkml/2024/12/6/828
1 parent b6970cb commit 1b5a1ae

File tree

1 file changed

+118
-42
lines changed

1 file changed

+118
-42
lines changed

src/backend/linux_raw/vdso.rs

Lines changed: 118 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub(super) struct Vdso {
3434
// Symbol table
3535
symtab: *const Elf_Sym,
3636
symstrings: *const u8,
37+
gnu_hash: *const u32,
3738
bucket: *const ElfHashEntry,
3839
chain: *const ElfHashEntry,
3940
nbucket: ElfHashEntry,
@@ -60,6 +61,16 @@ fn elf_hash(name: &CStr) -> u32 {
6061
h
6162
}
6263

64+
fn gnu_hash(name: &CStr) -> u32 {
65+
let mut h: u32 = 5381;
66+
for s in name.to_bytes() {
67+
h = h
68+
.wrapping_add(h.wrapping_mul(32))
69+
.wrapping_add(u32::from(*s));
70+
}
71+
h
72+
}
73+
6374
/// Create a `Vdso` value by parsing the vDSO at the `sysinfo_ehdr` address.
6475
fn init_from_sysinfo_ehdr() -> Option<Vdso> {
6576
// SAFETY: The auxv initialization code does extensive checks to ensure
@@ -80,6 +91,7 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
8091
pv_offset: 0,
8192
symtab: null(),
8293
symstrings: null(),
94+
gnu_hash: null(),
8395
bucket: null(),
8496
chain: null(),
8597
nbucket: 0,
@@ -159,6 +171,11 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
159171
)?
160172
.as_ptr();
161173
}
174+
DT_GNU_HASH => {
175+
vdso.gnu_hash =
176+
check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
177+
.as_ptr()
178+
}
162179
DT_VERSYM => {
163180
vdso.versym =
164181
check_raw_pointer::<u16>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
@@ -183,7 +200,10 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
183200
// `check_raw_pointer` will have checked these pointers for null,
184201
// however they could still be null if the expected dynamic table
185202
// entries are absent.
186-
if vdso.symstrings.is_null() || vdso.symtab.is_null() || hash.is_null() {
203+
if vdso.symstrings.is_null()
204+
|| vdso.symtab.is_null()
205+
|| (hash.is_null() && vdso.gnu_hash.is_null())
206+
{
187207
return None; // Failed
188208
}
189209

@@ -192,10 +212,20 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
192212
}
193213

194214
// Parse the hash table header.
195-
vdso.nbucket = *hash.add(0);
196-
//vdso.nchain = *hash.add(1);
197-
vdso.bucket = hash.add(2);
198-
vdso.chain = hash.add(vdso.nbucket as usize + 2);
215+
if !vdso.gnu_hash.is_null() {
216+
vdso.nbucket = *vdso.gnu_hash;
217+
// The bucket array is located after the header (4 uint32) and the bloom
218+
// filter (size_t array of gnu_hash[2] elements).
219+
vdso.bucket = vdso
220+
.gnu_hash
221+
.add(4)
222+
.add(size_of::<c::size_t>() / 4 * *vdso.gnu_hash.add(2) as usize);
223+
} else {
224+
vdso.nbucket = *hash.add(0);
225+
//vdso.nchain = *hash.add(1);
226+
vdso.bucket = hash.add(2);
227+
vdso.chain = hash.add(vdso.nbucket as usize + 2);
228+
}
199229

200230
// That's all we need.
201231
Some(vdso)
@@ -261,49 +291,96 @@ impl Vdso {
261291
&& (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast()))
262292
}
263293

294+
/// Check to see if the symbol is the one we're looking for.
295+
///
296+
/// # Safety
297+
///
298+
/// The raw pointers inside `self` must be valid.
299+
unsafe fn check_sym(
300+
&self,
301+
sym: &Elf_Sym,
302+
i: u32,
303+
name: &CStr,
304+
version: &CStr,
305+
ver_hash: u32,
306+
) -> bool {
307+
// Check for a defined global or weak function w/ right name.
308+
//
309+
// Accept `STT_NOTYPE` in addition to `STT_FUNC` for the symbol
310+
// type, for compatibility with some versions of Linux on
311+
// PowerPC64. See [this commit] in Linux for more background.
312+
//
313+
// [this commit]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/tools/testing/selftests/vDSO/parse_vdso.c?id=0161bd38c24312853ed5ae9a425a1c41c4ac674a
314+
if ELF_ST_TYPE(sym.st_info) != STT_FUNC && ELF_ST_TYPE(sym.st_info) != STT_NOTYPE {
315+
return false;
316+
}
317+
if ELF_ST_BIND(sym.st_info) != STB_GLOBAL && ELF_ST_BIND(sym.st_info) != STB_WEAK {
318+
return false;
319+
}
320+
if name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()) {
321+
return false;
322+
}
323+
324+
// Check symbol version.
325+
if !self.versym.is_null()
326+
&& !self.match_version(*self.versym.add(i as usize), version, ver_hash)
327+
{
328+
return false;
329+
}
330+
331+
true
332+
}
333+
264334
/// Look up a symbol in the vDSO.
265335
pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void {
266336
let ver_hash = elf_hash(version);
267-
let name_hash = elf_hash(name);
268337

269338
// SAFETY: The pointers in `self` must be valid.
270339
unsafe {
271-
let mut chain = *self
272-
.bucket
273-
.add((ElfHashEntry::from(name_hash) % self.nbucket) as usize);
274-
275-
while chain != ElfHashEntry::from(STN_UNDEF) {
276-
let sym = &*self.symtab.add(chain as usize);
277-
278-
// Check for a defined global or weak function w/ right name.
279-
//
280-
// Accept `STT_NOTYPE` in addition to `STT_FUNC` for the symbol
281-
// type, for compatibility with some versions of Linux on
282-
// PowerPC64. See [this commit] in Linux for more background.
283-
//
284-
// [this commit]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/tools/testing/selftests/vDSO/parse_vdso.c?id=0161bd38c24312853ed5ae9a425a1c41c4ac674a
285-
if (ELF_ST_TYPE(sym.st_info) != STT_FUNC &&
286-
ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
287-
|| (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
288-
&& ELF_ST_BIND(sym.st_info) != STB_WEAK)
289-
|| sym.st_shndx == SHN_UNDEF
290-
|| sym.st_shndx == SHN_ABS
291-
|| ELF_ST_VISIBILITY(sym.st_other) != STV_DEFAULT
292-
|| (name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()))
293-
// Check symbol version.
294-
|| (!self.versym.is_null()
295-
&& !self.match_version(*self.versym.add(chain as usize), version, ver_hash))
296-
{
297-
chain = *self.chain.add(chain as usize);
298-
continue;
299-
}
340+
if !self.gnu_hash.is_null() {
341+
let mut h1: u32 = gnu_hash(name);
300342

301-
let sum = self.addr_from_elf(sym.st_value).unwrap();
302-
assert!(
303-
sum as usize >= self.load_addr as usize
304-
&& sum as usize <= self.load_end as usize
305-
);
306-
return sum as *mut c::c_void;
343+
let mut i = *self.bucket.add((h1 % self.nbucket) as usize);
344+
if i == 0 {
345+
return null_mut();
346+
}
347+
h1 |= 1;
348+
let mut hashval = self
349+
.bucket
350+
.add(self.nbucket as usize)
351+
.add((i - *self.gnu_hash.add(1)) as usize);
352+
loop {
353+
let sym: &Elf_Sym = &*self.symtab.add(i as usize);
354+
let h2 = *hashval;
355+
hashval = hashval.add(1);
356+
if h1 == (h2 | 1) && self.check_sym(sym, i, name, version, ver_hash) {
357+
let sum = self.addr_from_elf(sym.st_value).unwrap();
358+
assert!(
359+
sum as usize >= self.load_addr as usize
360+
&& sum as usize <= self.load_end as usize
361+
);
362+
return sum as *mut c::c_void;
363+
}
364+
if (h2 & 1) != 0 {
365+
break;
366+
}
367+
i += 1;
368+
}
369+
} else {
370+
let mut i = *self.bucket.add((elf_hash(name) % self.nbucket) as usize);
371+
while i != 0 {
372+
let sym: &Elf_Sym = &*self.symtab.add(i as usize);
373+
if sym.st_shndx != SHN_UNDEF && self.check_sym(sym, i, name, version, ver_hash)
374+
{
375+
let sum = self.addr_from_elf(sym.st_value).unwrap();
376+
assert!(
377+
sum as usize >= self.load_addr as usize
378+
&& sum as usize <= self.load_end as usize
379+
);
380+
return sum as *mut c::c_void;
381+
}
382+
i = *self.chain.add(i as usize);
383+
}
307384
}
308385
}
309386

@@ -326,7 +403,6 @@ impl Vdso {
326403

327404
#[cfg(linux_raw)]
328405
#[test]
329-
#[ignore] // Until rustix is updated to the new vDSO format.
330406
fn test_vdso() {
331407
let vdso = Vdso::new().unwrap();
332408
assert!(!vdso.symtab.is_null());

0 commit comments

Comments
 (0)