@@ -34,6 +34,7 @@ pub(super) struct Vdso {
34
34
// Symbol table
35
35
symtab : * const Elf_Sym ,
36
36
symstrings : * const u8 ,
37
+ gnu_hash : * const u32 ,
37
38
bucket : * const ElfHashEntry ,
38
39
chain : * const ElfHashEntry ,
39
40
nbucket : ElfHashEntry ,
@@ -60,6 +61,16 @@ fn elf_hash(name: &CStr) -> u32 {
60
61
h
61
62
}
62
63
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
+
63
74
/// Create a `Vdso` value by parsing the vDSO at the `sysinfo_ehdr` address.
64
75
fn init_from_sysinfo_ehdr ( ) -> Option < Vdso > {
65
76
// SAFETY: The auxv initialization code does extensive checks to ensure
@@ -80,6 +91,7 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
80
91
pv_offset : 0 ,
81
92
symtab : null ( ) ,
82
93
symstrings : null ( ) ,
94
+ gnu_hash : null ( ) ,
83
95
bucket : null ( ) ,
84
96
chain : null ( ) ,
85
97
nbucket : 0 ,
@@ -159,6 +171,11 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
159
171
) ?
160
172
. as_ptr ( ) ;
161
173
}
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
+ }
162
179
DT_VERSYM => {
163
180
vdso. versym =
164
181
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> {
183
200
// `check_raw_pointer` will have checked these pointers for null,
184
201
// however they could still be null if the expected dynamic table
185
202
// 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
+ {
187
207
return None ; // Failed
188
208
}
189
209
@@ -192,10 +212,20 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
192
212
}
193
213
194
214
// 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
+ }
199
229
200
230
// That's all we need.
201
231
Some ( vdso)
@@ -261,49 +291,96 @@ impl Vdso {
261
291
&& ( name == CStr :: from_ptr ( self . symstrings . add ( aux. vda_name as usize ) . cast ( ) ) )
262
292
}
263
293
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
+
264
334
/// Look up a symbol in the vDSO.
265
335
pub ( super ) fn sym ( & self , version : & CStr , name : & CStr ) -> * mut c:: c_void {
266
336
let ver_hash = elf_hash ( version) ;
267
- let name_hash = elf_hash ( name) ;
268
337
269
338
// SAFETY: The pointers in `self` must be valid.
270
339
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) ;
300
342
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
+ }
307
384
}
308
385
}
309
386
@@ -326,7 +403,6 @@ impl Vdso {
326
403
327
404
#[ cfg( linux_raw) ]
328
405
#[ test]
329
- #[ ignore] // Until rustix is updated to the new vDSO format.
330
406
fn test_vdso ( ) {
331
407
let vdso = Vdso :: new ( ) . unwrap ( ) ;
332
408
assert ! ( !vdso. symtab. is_null( ) ) ;
0 commit comments