1
1
//! Parse the Linux vDSO.
2
2
//!
3
3
//! The following code is transliterated from
4
- //! tools/testing/selftests/vDSO/parse_vdso.c in Linux 5.11 , which is licensed
4
+ //! tools/testing/selftests/vDSO/parse_vdso.c in Linux 6.12 , which is licensed
5
5
//! with Creative Commons Zero License, version 1.0,
6
6
//! available at <https://creativecommons.org/publicdomain/zero/1.0/legalcode>
7
7
//!
@@ -20,6 +20,11 @@ use core::mem::size_of;
20
20
use core:: ptr:: { null, null_mut} ;
21
21
use linux_raw_sys:: elf:: * ;
22
22
23
+ #[ cfg( target_arch = "s390x" ) ]
24
+ type ElfHashEntry = u64 ;
25
+ #[ cfg( not( target_arch = "s390x" ) ) ]
26
+ type ElfHashEntry = u32 ;
27
+
23
28
pub ( super ) struct Vdso {
24
29
// Load information
25
30
load_addr : * const Elf_Ehdr ,
@@ -29,17 +34,19 @@ pub(super) struct Vdso {
29
34
// Symbol table
30
35
symtab : * const Elf_Sym ,
31
36
symstrings : * const u8 ,
32
- bucket : * const u32 ,
33
- chain : * const u32 ,
34
- nbucket : u32 ,
35
- //nchain: u32 ,
37
+ bucket : * const ElfHashEntry ,
38
+ chain : * const ElfHashEntry ,
39
+ nbucket : ElfHashEntry ,
40
+ //nchain: ElfHashEntry ,
36
41
37
42
// Version table
38
43
versym : * const u16 ,
39
44
verdef : * const Elf_Verdef ,
40
45
}
41
46
42
- // Straight from the ELF specification.
47
+ /// Straight from the ELF specification...and then tweaked slightly, in order to
48
+ /// avoid a few clang warnings.
49
+ /// (And then translated to Rust).
43
50
fn elf_hash ( name : & CStr ) -> u32 {
44
51
let mut h: u32 = 0 ;
45
52
for b in name. to_bytes ( ) {
@@ -91,11 +98,6 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
91
98
let mut found_vaddr = false ;
92
99
for i in 0 ..hdr. e_phnum {
93
100
let phdr = & * pt. add ( i as usize ) ;
94
- if phdr. p_flags & PF_W != 0 {
95
- // Don't trust any vDSO that claims to be loading writable
96
- // segments into memory.
97
- return None ;
98
- }
99
101
if phdr. p_type == PT_LOAD && !found_vaddr {
100
102
// The segment should be readable and executable, because it
101
103
// contains the symbol table and the function bodies.
@@ -129,7 +131,7 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
129
131
}
130
132
131
133
// Fish out the useful bits of the dynamic table.
132
- let mut hash: * const u32 = null ( ) ;
134
+ let mut hash: * const ElfHashEntry = null ( ) ;
133
135
vdso. symstrings = null ( ) ;
134
136
vdso. symtab = null ( ) ;
135
137
vdso. versym = null ( ) ;
@@ -152,8 +154,10 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
152
154
. as_ptr ( ) ;
153
155
}
154
156
DT_HASH => {
155
- hash = check_raw_pointer :: < u32 > ( vdso. addr_from_elf ( d. d_un . d_ptr ) ? as * mut _ ) ?
156
- . as_ptr ( ) ;
157
+ hash = check_raw_pointer :: < ElfHashEntry > (
158
+ vdso. addr_from_elf ( d. d_un . d_ptr ) ? as * mut _
159
+ ) ?
160
+ . as_ptr ( ) ;
157
161
}
158
162
DT_VERSYM => {
159
163
vdso. versym =
@@ -176,8 +180,12 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
176
180
}
177
181
i = i. checked_add ( 1 ) ?;
178
182
}
179
- // The upstream code checks `symstrings`, `symtab`, and `hash` for
180
- // null; here, `check_raw_pointer` has already done that.
183
+ // `check_raw_pointer` will have checked these pointers for null,
184
+ // however they could still be null if the expected dynamic table
185
+ // entries are absent.
186
+ if vdso. symstrings . is_null ( ) || vdso. symtab . is_null ( ) || hash. is_null ( ) {
187
+ return None ; // Failed
188
+ }
181
189
182
190
if vdso. verdef . is_null ( ) {
183
191
vdso. versym = null ( ) ;
@@ -260,16 +268,20 @@ impl Vdso {
260
268
261
269
// SAFETY: The pointers in `self` must be valid.
262
270
unsafe {
263
- let mut chain = * self . bucket . add ( ( name_hash % self . nbucket ) as usize ) ;
271
+ let mut chain = * self
272
+ . bucket
273
+ . add ( ( ElfHashEntry :: from ( name_hash) % self . nbucket ) as usize ) ;
264
274
265
- while chain != STN_UNDEF {
275
+ while chain != ElfHashEntry :: from ( STN_UNDEF ) {
266
276
let sym = & * self . symtab . add ( chain as usize ) ;
267
277
268
278
// Check for a defined global or weak function w/ right name.
269
279
//
270
- // The reference parser in Linux's parse_vdso.c requires
271
- // symbols to have type `STT_FUNC`, but on powerpc64, the vDSO
272
- // uses `STT_NOTYPE`, so allow that too.
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
273
285
if ( ELF_ST_TYPE ( sym. st_info ) != STT_FUNC &&
274
286
ELF_ST_TYPE ( sym. st_info ) != STT_NOTYPE )
275
287
|| ( ELF_ST_BIND ( sym. st_info ) != STB_GLOBAL
@@ -311,3 +323,33 @@ impl Vdso {
311
323
self . base_plus ( elf_addr. wrapping_add ( self . pv_offset ) )
312
324
}
313
325
}
326
+
327
+ #[ cfg( linux_raw) ]
328
+ #[ test]
329
+ #[ ignore] // Until rustix is updated to the new vDSO format.
330
+ fn test_vdso ( ) {
331
+ let vdso = Vdso :: new ( ) . unwrap ( ) ;
332
+ assert ! ( !vdso. symtab. is_null( ) ) ;
333
+ assert ! ( !vdso. symstrings. is_null( ) ) ;
334
+
335
+ #[ cfg( target_arch = "x86_64" ) ]
336
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_clock_gettime" ) ) ;
337
+ #[ cfg( target_arch = "arm" ) ]
338
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_clock_gettime64" ) ) ;
339
+ #[ cfg( target_arch = "aarch64" ) ]
340
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6.39" ) , cstr ! ( "__kernel_clock_gettime" ) ) ;
341
+ #[ cfg( target_arch = "x86" ) ]
342
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_clock_gettime64" ) ) ;
343
+ #[ cfg( target_arch = "riscv64" ) ]
344
+ let ptr = vdso. sym ( cstr ! ( "LINUX_4.15" ) , cstr ! ( "__vdso_clock_gettime" ) ) ;
345
+ #[ cfg( target_arch = "powerpc64" ) ]
346
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6.15" ) , cstr ! ( "__kernel_clock_gettime" ) ) ;
347
+ #[ cfg( target_arch = "s390x" ) ]
348
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6.29" ) , cstr ! ( "__kernel_clock_gettime" ) ) ;
349
+ #[ cfg( any( target_arch = "mips" , target_arch = "mips32r6" ) ) ]
350
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_clock_gettime64" ) ) ;
351
+ #[ cfg( any( target_arch = "mips64" , target_arch = "mips64r6" ) ) ]
352
+ let ptr = vdso. sym ( cstr ! ( "LINUX_2.6" ) , cstr ! ( "__vdso_clock_gettime" ) ) ;
353
+
354
+ assert ! ( !ptr. is_null( ) ) ;
355
+ }
0 commit comments