@@ -35,31 +35,50 @@ fn infer_current_exe(base_addr: usize) -> OsString {
3535 env:: current_exe ( ) . map ( |e| e. into ( ) ) . unwrap_or_default ( )
3636}
3737
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) ]
4042unsafe extern "C" fn callback (
4143 info : * mut libc:: dl_phdr_info ,
4244 _size : libc:: size_t ,
4345 vec : * mut libc:: c_void ,
4446) -> 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 ( ) {
5670 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 ( )
5774 }
75+ } ;
76+ let headers = if dlpi_phdr. is_null ( ) || dlpi_phnum == 0 {
77+ & [ ]
5878 } 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 ) }
6181 } ;
62- let headers = slice:: from_raw_parts ( info. dlpi_phdr , info. dlpi_phnum as usize ) ;
6382 libs. push ( Library {
6483 name,
6584 segments : headers
@@ -69,7 +88,7 @@ unsafe extern "C" fn callback(
6988 stated_virtual_memory_address : ( * header) . p_vaddr as usize ,
7089 } )
7190 . collect ( ) ,
72- bias : info . dlpi_addr as usize ,
91+ bias : dlpi_addr as usize ,
7392 } ) ;
7493 0
7594}
0 commit comments