1 #![allow(deprecated)]
2 
3 use super::mystd::ffi::{CStr, OsStr};
4 use super::mystd::os::unix::prelude::*;
5 use super::mystd::prelude::v1::*;
6 use super::{Library, LibrarySegment};
7 use core::convert::TryInto;
8 use core::mem;
9 
native_libraries() -> Vec<Library>10 pub(super) fn native_libraries() -> Vec<Library> {
11     let mut ret = Vec::new();
12     let images = unsafe { libc::_dyld_image_count() };
13     for i in 0..images {
14         ret.extend(native_library(i));
15     }
16     return ret;
17 }
18 
native_library(i: u32) -> Option<Library>19 fn native_library(i: u32) -> Option<Library> {
20     use object::macho;
21     use object::read::macho::{MachHeader, Segment};
22     use object::NativeEndian;
23 
24     // Fetch the name of this library which corresponds to the path of
25     // where to load it as well.
26     let name = unsafe {
27         let name = libc::_dyld_get_image_name(i);
28         if name.is_null() {
29             return None;
30         }
31         CStr::from_ptr(name)
32     };
33 
34     // Load the image header of this library and delegate to `object` to
35     // parse all the load commands so we can figure out all the segments
36     // involved here.
37     let (mut load_commands, endian) = unsafe {
38         let header = libc::_dyld_get_image_header(i);
39         if header.is_null() {
40             return None;
41         }
42         match (*header).magic {
43             macho::MH_MAGIC => {
44                 let endian = NativeEndian;
45                 let header = &*(header as *const macho::MachHeader32<NativeEndian>);
46                 let data = core::slice::from_raw_parts(
47                     header as *const _ as *const u8,
48                     mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
49                 );
50                 (header.load_commands(endian, data).ok()?, endian)
51             }
52             macho::MH_MAGIC_64 => {
53                 let endian = NativeEndian;
54                 let header = &*(header as *const macho::MachHeader64<NativeEndian>);
55                 let data = core::slice::from_raw_parts(
56                     header as *const _ as *const u8,
57                     mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
58                 );
59                 (header.load_commands(endian, data).ok()?, endian)
60             }
61             _ => return None,
62         }
63     };
64 
65     // Iterate over the segments and register known regions for segments
66     // that we find. Additionally record information bout text segments
67     // for processing later, see comments below.
68     let mut segments = Vec::new();
69     let mut first_text = 0;
70     let mut text_fileoff_zero = false;
71     while let Some(cmd) = load_commands.next().ok()? {
72         if let Some((seg, _)) = cmd.segment_32().ok()? {
73             if seg.name() == b"__TEXT" {
74                 first_text = segments.len();
75                 if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
76                     text_fileoff_zero = true;
77                 }
78             }
79             segments.push(LibrarySegment {
80                 len: seg.vmsize(endian).try_into().ok()?,
81                 stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
82             });
83         }
84         if let Some((seg, _)) = cmd.segment_64().ok()? {
85             if seg.name() == b"__TEXT" {
86                 first_text = segments.len();
87                 if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
88                     text_fileoff_zero = true;
89                 }
90             }
91             segments.push(LibrarySegment {
92                 len: seg.vmsize(endian).try_into().ok()?,
93                 stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
94             });
95         }
96     }
97 
98     // Determine the "slide" for this library which ends up being the
99     // bias we use to figure out where in memory objects are loaded.
100     // This is a bit of a weird computation though and is the result of
101     // trying a few things in the wild and seeing what sticks.
102     //
103     // The general idea is that the `bias` plus a segment's
104     // `stated_virtual_memory_address` is going to be where in the
105     // actual address space the segment resides. The other thing we rely
106     // on though is that a real address minus the `bias` is the index to
107     // look up in the symbol table and debuginfo.
108     //
109     // It turns out, though, that for system loaded libraries these
110     // calculations are incorrect. For native executables, however, it
111     // appears correct. Lifting some logic from LLDB's source it has
112     // some special-casing for the first `__TEXT` section loaded from
113     // file offset 0 with a nonzero size. For whatever reason when this
114     // is present it appears to mean that the symbol table is relative
115     // to just the vmaddr slide for the library. If it's *not* present
116     // then the symbol table is relative to the the vmaddr slide plus
117     // the segment's stated address.
118     //
119     // To handle this situation if we *don't* find a text section at
120     // file offset zero then we increase the bias by the first text
121     // sections's stated address and decrease all stated addresses by
122     // that amount as well. That way the symbol table is always appears
123     // relative to the library's bias amount. This appears to have the
124     // right results for symbolizing via the symbol table.
125     //
126     // Honestly I'm not entirely sure whether this is right or if
127     // there's something else that should indicate how to do this. For
128     // now though this seems to work well enough (?) and we should
129     // always be able to tweak this over time if necessary.
130     //
131     // For some more information see #318
132     let mut slide = unsafe { libc::_dyld_get_image_vmaddr_slide(i) as usize };
133     if !text_fileoff_zero {
134         let adjust = segments[first_text].stated_virtual_memory_address;
135         for segment in segments.iter_mut() {
136             segment.stated_virtual_memory_address -= adjust;
137         }
138         slide += adjust;
139     }
140 
141     Some(Library {
142         name: OsStr::from_bytes(name.to_bytes()).to_owned(),
143         segments,
144         bias: slide,
145     })
146 }
147