Skip to content

Commit 688e20e

Browse files
authored
Remove dependency on findshlibs (#314)
This is a pretty small dependency for the `gimli-symbolize` feature, and in an effort to reduce the number of external dependencies this commit inlines the small amount of code we used from that crate here directly. This should hopefully make this a bit easier to port to libstd eventually.
1 parent 8af627d commit 688e20e

File tree

2 files changed

+134
-45
lines changed

2 files changed

+134
-45
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ cpp_demangle = { default-features = false, version = "0.2.3", optional = true }
3535

3636
# Optional dependencies enabled through the `gimli-symbolize` feature
3737
addr2line = { version = "0.11.0", optional = true, default-features = false, features = ['std'] }
38-
findshlibs = { version = "0.5.0", optional = true }
3938
goblin = { version = "0.2", optional = true, default-features = false, features = ['elf32', 'elf64', 'mach32', 'mach64', 'pe32', 'pe64', 'std'] }
4039

4140
[target.'cfg(windows)'.dependencies]
@@ -96,7 +95,7 @@ kernel32 = []
9695
libbacktrace = ["backtrace-sys/backtrace-sys"]
9796
dladdr = []
9897
coresymbolication = []
99-
gimli-symbolize = ["addr2line", "findshlibs", "goblin"]
98+
gimli-symbolize = ["addr2line", "goblin"]
10099

101100
#=======================================
102101
# Methods of serialization

src/symbolize/gimli.rs

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::SymbolName;
1414
use addr2line::gimli;
1515
use core::mem;
1616
use core::u32;
17-
use findshlibs::{self, Segment, SharedLibrary};
1817
use libc::c_void;
1918
use std::convert::TryInto;
2019
use std::env;
@@ -160,8 +159,14 @@ cfg_if::cfg_if! {
160159
Some(self.symbols[i].1.name(&self.strtab).ok()?.as_bytes())
161160
}
162161
}
162+
163+
fn native_libraries() -> Vec<Library> {
164+
Vec::new()
165+
}
163166
} else if #[cfg(target_os = "macos")] {
164167
use goblin::mach::MachO;
168+
use std::os::unix::prelude::*;
169+
use std::ffi::{OsStr, CStr};
165170

166171
struct Object<'a> {
167172
macho: MachO<'a>,
@@ -221,8 +226,78 @@ cfg_if::cfg_if! {
221226
Some(sym.as_bytes())
222227
}
223228
}
229+
230+
#[allow(deprecated)]
231+
fn native_libraries() -> Vec<Library> {
232+
let mut ret = Vec::new();
233+
unsafe {
234+
for i in 0..libc::_dyld_image_count() {
235+
ret.extend(native_library(i));
236+
}
237+
}
238+
return ret;
239+
}
240+
241+
#[allow(deprecated)]
242+
unsafe fn native_library(i: u32) -> Option<Library> {
243+
let name = libc::_dyld_get_image_name(i);
244+
if name.is_null() {
245+
return None;
246+
}
247+
let name = CStr::from_ptr(name);
248+
let header = libc::_dyld_get_image_header(i);
249+
if header.is_null() {
250+
return None;
251+
}
252+
let mut segments = Vec::new();
253+
match (*header).magic {
254+
libc::MH_MAGIC => {
255+
let mut next_cmd = header.offset(1) as *const libc::load_command;
256+
for _ in 0..(*header).ncmds {
257+
segments.extend(segment(next_cmd));
258+
next_cmd = (next_cmd as usize + (*next_cmd).cmdsize as usize) as *const _;
259+
}
260+
}
261+
libc::MH_MAGIC_64 => {
262+
let header = header as *const libc::mach_header_64;
263+
let mut next_cmd = header.offset(1) as *const libc::load_command;
264+
for _ in 0..(*header).ncmds {
265+
segments.extend(segment(next_cmd));
266+
next_cmd = (next_cmd as usize + (*next_cmd).cmdsize as usize) as *const _;
267+
}
268+
}
269+
_ => return None,
270+
}
271+
Some(Library {
272+
name: OsStr::from_bytes(name.to_bytes()).to_owned(),
273+
segments,
274+
bias: libc::_dyld_get_image_vmaddr_slide(i) as *const u8,
275+
})
276+
}
277+
278+
unsafe fn segment(cmd: *const libc::load_command) -> Option<LibrarySegment> {
279+
Some(match (*cmd).cmd {
280+
libc::LC_SEGMENT => {
281+
let cmd = cmd as *const libc::segment_command;
282+
LibrarySegment {
283+
len: (*cmd).vmsize as usize,
284+
stated_virtual_memory_address: (*cmd).vmaddr as *const u8,
285+
}
286+
}
287+
libc::LC_SEGMENT_64 => {
288+
let cmd = cmd as *const libc::segment_command_64;
289+
LibrarySegment {
290+
len: (*cmd).vmsize as usize,
291+
stated_virtual_memory_address: (*cmd).vmaddr as *const u8,
292+
}
293+
}
294+
_ => return None,
295+
})
296+
}
224297
} else {
225298
use goblin::elf::Elf;
299+
use std::os::unix::prelude::*;
300+
use std::ffi::{OsStr, CStr};
226301

227302
struct Object<'a> {
228303
elf: Elf<'a>,
@@ -300,6 +375,45 @@ cfg_if::cfg_if! {
300375
}
301376
}
302377
}
378+
379+
fn native_libraries() -> Vec<Library> {
380+
let mut ret = Vec::new();
381+
unsafe {
382+
libc::dl_iterate_phdr(Some(callback), &mut ret as *mut _ as *mut _);
383+
}
384+
return ret;
385+
}
386+
387+
unsafe extern "C" fn callback(
388+
info: *mut libc::dl_phdr_info,
389+
_size: libc::size_t,
390+
vec: *mut libc::c_void,
391+
) -> libc::c_int {
392+
let libs = &mut *(vec as *mut Vec<Library>);
393+
let name = if (*info).dlpi_name.is_null() || *(*info).dlpi_name == 0{
394+
if libs.is_empty() {
395+
std::env::current_exe().map(|e| e.into()).unwrap_or_default()
396+
} else {
397+
OsString::new()
398+
}
399+
} else {
400+
let bytes = CStr::from_ptr((*info).dlpi_name).to_bytes();
401+
OsStr::from_bytes(bytes).to_owned()
402+
};
403+
let headers = std::slice::from_raw_parts((*info).dlpi_phdr, (*info).dlpi_phnum as usize);
404+
libs.push(Library {
405+
name,
406+
segments: headers
407+
.iter()
408+
.map(|header| LibrarySegment {
409+
len: (*header).p_memsz as usize,
410+
stated_virtual_memory_address: (*header).p_vaddr as *const u8,
411+
})
412+
.collect(),
413+
bias: (*info).dlpi_addr as *const u8,
414+
});
415+
0
416+
}
303417
}
304418
}
305419

@@ -402,12 +516,12 @@ struct Cache {
402516
struct Library {
403517
name: OsString,
404518
segments: Vec<LibrarySegment>,
405-
bias: findshlibs::Bias,
519+
bias: *const u8,
406520
}
407521

408522
struct LibrarySegment {
409523
len: usize,
410-
stated_virtual_memory_address: findshlibs::Svma,
524+
stated_virtual_memory_address: *const u8,
411525
}
412526

413527
// unsafe because this is required to be externally synchronized
@@ -417,29 +531,9 @@ pub unsafe fn clear_symbol_cache() {
417531

418532
impl Cache {
419533
fn new() -> Cache {
420-
let mut libraries = Vec::new();
421-
// Iterate over all loaded shared libraries and cache information we
422-
// learn about them. This way we only load information here once instead
423-
// of once per symbol.
424-
findshlibs::TargetSharedLibrary::each(|so| {
425-
use findshlibs::IterationControl::*;
426-
libraries.push(Library {
427-
name: so.name().to_owned(),
428-
segments: so
429-
.segments()
430-
.map(|s| LibrarySegment {
431-
len: s.len(),
432-
stated_virtual_memory_address: s.stated_virtual_memory_address(),
433-
})
434-
.collect(),
435-
bias: so.virtual_memory_bias(),
436-
});
437-
Continue
438-
});
439-
440534
Cache {
441535
mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE),
442-
libraries,
536+
libraries: native_libraries(),
443537
}
444538
}
445539

@@ -460,14 +554,14 @@ impl Cache {
460554
f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new()))
461555
}
462556

463-
fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, findshlibs::Svma)> {
464-
// Note that for now `findshlibs` is unimplemented on Windows, so we
465-
// just unhelpfully assume that the address is an SVMA. Surprisingly it
466-
// seems to at least somewhat work on Wine on Linux though...
557+
fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> {
558+
// Note that we don't implement iterating native libraries on Windows,
559+
// so we just unhelpfully assume that the address is an SVMA.
560+
// Surprisingly it seems to at least somewhat work on Wine on Linux
561+
// though...
467562
//
468563
// This probably means ASLR on Windows is busted.
469564
if cfg!(windows) {
470-
let addr = findshlibs::Svma(addr);
471565
return Some((usize::max_value(), addr));
472566
}
473567

@@ -478,24 +572,20 @@ impl Cache {
478572
// First up, test if this `lib` has any segment containing the
479573
// `addr` (handling relocation). If this check passes then we
480574
// can continue below and actually translate the address.
481-
//
482-
// Note that this is an inlining of `contains_avma` in the
483-
// `findshlibs` crate here.
484575
if !lib.segments.iter().any(|s| {
485-
let svma = s.stated_virtual_memory_address;
486-
let start = unsafe { svma.0.offset(lib.bias.0) as usize };
576+
let svma = s.stated_virtual_memory_address as usize;
577+
let start = svma + lib.bias as usize;
487578
let end = start + s.len;
488579
let address = addr as usize;
489580
start <= address && address < end
490581
}) {
491582
return None;
492583
}
493584

494-
// Now that we know `lib` contains `addr`, do the equiavlent of
495-
// `avma_to_svma` in the `findshlibs` crate.
496-
let reverse_bias = -lib.bias.0;
497-
let svma = findshlibs::Svma(unsafe { addr.offset(reverse_bias) });
498-
Some((i, svma))
585+
// Now that we know `lib` contains `addr`, we can offset with
586+
// the bias to find the stated virutal memory address.
587+
let svma = addr as usize - lib.bias as usize;
588+
Some((i, svma as *const u8))
499589
})
500590
.next()
501591
}
@@ -560,20 +650,20 @@ pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
560650
Some(cx) => cx,
561651
None => return,
562652
};
563-
if let Ok(mut frames) = cx.dwarf.find_frames(addr.0 as u64) {
653+
if let Ok(mut frames) = cx.dwarf.find_frames(addr as u64) {
564654
while let Ok(Some(frame)) = frames.next() {
565655
cb.call(Symbol::Frame {
566-
addr: addr.0 as *mut c_void,
656+
addr: addr as *mut c_void,
567657
location: frame.location,
568658
name: frame.function.map(|f| f.name.slice()),
569659
});
570660
}
571661
}
572662

573663
if !cb.called {
574-
if let Some(name) = cx.object.search_symtab(addr.0 as u64) {
664+
if let Some(name) = cx.object.search_symtab(addr as u64) {
575665
cb.call(Symbol::Symtab {
576-
addr: addr.0 as *mut c_void,
666+
addr: addr as *mut c_void,
577667
name,
578668
});
579669
}

0 commit comments

Comments
 (0)