@@ -35,31 +35,50 @@ fn infer_current_exe(base_addr: usize) -> OsString {
35
35
env:: current_exe ( ) . map ( |e| e. into ( ) ) . unwrap_or_default ( )
36
36
}
37
37
38
- // `info` should be a valid pointers.
39
- // `vec` should be a valid pointer to a `std::Vec`.
38
+ /// # Safety
39
+ /// `info` must be a valid pointer.
40
+ /// `vec` must be a valid pointer to `Vec<Library>`
41
+ #[ forbid( unsafe_op_in_unsafe_fn) ]
40
42
unsafe extern "C" fn callback (
41
43
info : * mut libc:: dl_phdr_info ,
42
44
_size : libc:: size_t ,
43
45
vec : * mut libc:: c_void ,
44
46
) -> libc:: c_int {
45
- let info = & * info;
46
- let libs = & mut * vec. cast :: < Vec < Library > > ( ) ;
47
- let is_main_prog = info. dlpi_name . is_null ( ) || * info. dlpi_name == 0 ;
48
- let name = if is_main_prog {
49
- // The man page for dl_iterate_phdr says that the first object visited by
50
- // callback is the main program; so the first time we encounter a
51
- // nameless entry, we can assume its the main program and try to infer its path.
52
- // After that, we cannot continue that assumption, and we use an empty string.
53
- if libs. is_empty ( ) {
54
- infer_current_exe ( info. dlpi_addr as usize )
55
- } else {
47
+ // SAFETY: We are guaranteed these fields:
48
+ let dlpi_addr = unsafe { ( * info) . dlpi_addr } ;
49
+ let dlpi_name = unsafe { ( * info) . dlpi_name } ;
50
+ let dlpi_phdr = unsafe { ( * info) . dlpi_phdr } ;
51
+ let dlpi_phnum = unsafe { ( * info) . dlpi_phnum } ;
52
+ // SAFETY: We assured this.
53
+ let libs = unsafe { & mut * vec. cast :: < Vec < Library > > ( ) } ;
54
+ // most implementations give us the main program first
55
+ let is_main = libs. is_empty ( ) ;
56
+ // we may be statically linked, which means we are main and mostly one big blob of code
57
+ let is_static = dlpi_addr == 0 ;
58
+ // sometimes we get a null or 0-len CStr, based on libc's whims, but these mean the same thing
59
+ let no_given_name = dlpi_name. is_null ( )
60
+ // SAFETY: we just checked for null
61
+ || unsafe { * dlpi_name == 0 } ;
62
+ let name = if is_static {
63
+ // don't try to look up our name from /proc/self/maps, it'll get silly
64
+ env:: current_exe ( ) . unwrap_or_default ( ) . into_os_string ( )
65
+ } else if is_main && no_given_name {
66
+ infer_current_exe ( dlpi_addr as usize )
67
+ } else {
68
+ // this fallback works even if we are main, because some platforms give the name anyways
69
+ if dlpi_name. is_null ( ) {
56
70
OsString :: new ( )
71
+ } else {
72
+ // SAFETY: we just checked for nullness
73
+ OsStr :: from_bytes ( unsafe { CStr :: from_ptr ( dlpi_name) } . to_bytes ( ) ) . to_owned ( )
57
74
}
75
+ } ;
76
+ let headers = if dlpi_phdr. is_null ( ) || dlpi_phnum == 0 {
77
+ & [ ]
58
78
} else {
59
- let bytes = CStr :: from_ptr ( info . dlpi_name ) . to_bytes ( ) ;
60
- OsStr :: from_bytes ( bytes ) . to_owned ( )
79
+ // SAFETY: We just checked for nullness or 0-len slices
80
+ unsafe { slice :: from_raw_parts ( dlpi_phdr , dlpi_phnum as usize ) }
61
81
} ;
62
- let headers = slice:: from_raw_parts ( info. dlpi_phdr , info. dlpi_phnum as usize ) ;
63
82
libs. push ( Library {
64
83
name,
65
84
segments : headers
@@ -69,7 +88,7 @@ unsafe extern "C" fn callback(
69
88
stated_virtual_memory_address : ( * header) . p_vaddr as usize ,
70
89
} )
71
90
. collect ( ) ,
72
- bias : info . dlpi_addr as usize ,
91
+ bias : dlpi_addr as usize ,
73
92
} ) ;
74
93
0
75
94
}
0 commit comments