@@ -9,52 +9,128 @@ use crate::types::BytesOrWideString;
9
9
use crate :: SymbolName ;
10
10
use addr2line;
11
11
use addr2line:: object:: { self , Object } ;
12
+ use core:: cell:: RefCell ;
13
+ use core:: mem;
14
+ use core:: u32;
12
15
use findshlibs:: { self , Segment , SharedLibrary } ;
13
16
use libc:: c_void;
14
17
use memmap:: Mmap ;
15
- use std:: cell:: RefCell ;
16
18
use std:: env;
17
19
use std:: fs:: File ;
18
- use std:: mem;
19
20
use std:: path:: { Path , PathBuf } ;
20
21
use std:: prelude:: v1:: * ;
21
- use std:: u32;
22
22
23
23
const MAPPINGS_CACHE_SIZE : usize = 4 ;
24
24
25
- type Dwarf = addr2line:: Context ;
26
25
type Symbols < ' map > = object:: SymbolMap < ' map > ;
27
26
28
27
struct Mapping {
29
- dwarf : Dwarf ,
28
+ dwarf : addr2line :: Context ,
30
29
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
31
30
symbols : Symbols < ' static > ,
32
31
_map : Mmap ,
33
32
}
34
33
34
+ macro_rules! mk {
35
+ ( Mapping { $map: expr, $object: expr } ) => { {
36
+ Mapping {
37
+ dwarf: addr2line:: Context :: new( & $object) . ok( ) ?,
38
+ // Convert to 'static lifetimes since the symbols should
39
+ // only borrow `map` and we're preserving `map` below.
40
+ //
41
+ // TODO: how do we know that `symbol_map` *only* borrows `map`?
42
+ symbols: unsafe { mem:: transmute:: <Symbols , Symbols <' static >>( $object. symbol_map( ) ) } ,
43
+ _map: $map,
44
+ }
45
+ } } ;
46
+ }
47
+
48
+ fn mmap ( path : & Path ) -> Option < Mmap > {
49
+ let file = File :: open ( path) . ok ( ) ?;
50
+ // TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25
51
+ unsafe { Mmap :: map ( & file) . ok ( ) }
52
+
53
+ }
54
+
35
55
impl Mapping {
36
- fn new ( path : & PathBuf ) -> Option < Mapping > {
37
- let file = File :: open ( path) . ok ( ) ?;
38
- // TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25
39
- let map = unsafe { Mmap :: map ( & file) . ok ( ) ? } ;
40
- let ( dwarf, symbols) = {
41
- let object = object:: ElfFile :: parse ( & * map) . ok ( ) ?;
42
- let dwarf = addr2line:: Context :: new ( & object) . ok ( ) ?;
43
- let symbols = object. symbol_map ( ) ;
44
- // Convert to 'static lifetimes.
45
- ( dwarf, unsafe { mem:: transmute ( symbols) } )
46
- } ;
47
- Some ( Mapping {
48
- dwarf,
49
- symbols,
50
- _map : map,
51
- } )
56
+ fn new ( path : & Path ) -> Option < Mapping > {
57
+ if cfg ! ( target_os = "macos" ) {
58
+ Mapping :: new_find_dsym ( path)
59
+ } else {
60
+ let map = mmap ( path) ?;
61
+ let object = object:: ElfFile :: parse ( & map) . ok ( ) ?;
62
+ Some ( mk ! ( Mapping { map, object } ) )
63
+ }
64
+ }
65
+
66
+ fn new_find_dsym ( path : & Path ) -> Option < Mapping > {
67
+ // First up we need to load the unique UUID which is stored in the macho
68
+ // header of the file we're reading, specified at `path`.
69
+ let map = mmap ( path) ?;
70
+ let object = object:: MachOFile :: parse ( & map) . ok ( ) ?;
71
+ let uuid = get_uuid ( & object) ?;
72
+
73
+ // Next we need to look for a `*.dSYM` file. For now we just probe the
74
+ // containing directory and look around for something that matches
75
+ // `*.dSYM`. Once it's found we root through the dwarf resources that it
76
+ // contains and try to find a macho file which has a matching UUID as
77
+ // the one of our own file. If we find a match that's the dwarf file we
78
+ // want to return.
79
+ let parent = path. parent ( ) ?;
80
+ for entry in parent. read_dir ( ) . ok ( ) ? {
81
+ let entry = entry. ok ( ) ?;
82
+ let filename = match entry. file_name ( ) . into_string ( ) {
83
+ Ok ( name) => name,
84
+ Err ( _) => continue ,
85
+ } ;
86
+ if !filename. ends_with ( ".dSYM" ) {
87
+ continue ;
88
+ }
89
+ let candidates = entry. path ( ) . join ( "Contents/Resources/DWARF" ) ;
90
+ if let Some ( mapping) = load_dsym ( & candidates, & uuid) {
91
+ return Some ( mapping) ;
92
+ }
93
+ }
94
+
95
+ // Looks like nothing matched our UUID, so let's at least return our own
96
+ // file. This should have the symbol table for at least some
97
+ // symbolication purposes.
98
+ return Some ( mk ! ( Mapping { map, object } ) ) ;
99
+
100
+ fn load_dsym ( dir : & Path , uuid : & [ u8 ] ) -> Option < Mapping > {
101
+ for entry in dir. read_dir ( ) . ok ( ) ? {
102
+ let entry = entry. ok ( ) ?;
103
+ let map = mmap ( & entry. path ( ) ) ?;
104
+ let object = object:: MachOFile :: parse ( & map) . ok ( ) ?;
105
+ let entry_uuid = get_uuid ( & object) ?;
106
+ if & entry_uuid[ ..] != uuid {
107
+ continue ;
108
+ }
109
+ return Some ( mk ! ( Mapping { map, object } ) ) ;
110
+ }
111
+
112
+ None
113
+ }
114
+
115
+ fn get_uuid ( object : & object:: MachOFile ) -> Option < [ u8 ; 16 ] > {
116
+ use goblin:: mach:: load_command:: CommandVariant ;
117
+
118
+ object
119
+ . macho ( )
120
+ . load_commands
121
+ . iter ( )
122
+ . filter_map ( |cmd| match cmd. command {
123
+ CommandVariant :: Uuid ( u) => Some ( u. uuid ) ,
124
+ _ => None ,
125
+ } )
126
+ . next ( )
127
+ }
52
128
}
53
129
54
130
// Ensure the 'static lifetimes don't leak.
55
131
fn rent < F > ( & self , mut f : F )
56
132
where
57
- F : FnMut ( & Dwarf , & Symbols ) ,
133
+ F : FnMut ( & addr2line :: Context , & Symbols ) ,
58
134
{
59
135
f ( & self . dwarf , & self . symbols )
60
136
}
@@ -77,7 +153,7 @@ thread_local! {
77
153
78
154
fn with_mapping_for_path < F > ( path : PathBuf , f : F )
79
155
where
80
- F : FnMut ( & Dwarf , & Symbols ) ,
156
+ F : FnMut ( & addr2line :: Context , & Symbols ) ,
81
157
{
82
158
MAPPINGS_CACHE . with ( |cache| {
83
159
let mut cache = cache. borrow_mut ( ) ;
0 commit comments