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