1
- use super :: { Context , Mapping , Path , Stash , Vec } ;
2
- use core:: convert:: TryFrom ;
3
- use object:: elf:: { ELFCOMPRESS_ZLIB , SHF_COMPRESSED } ;
1
+ use super :: mystd:: ffi:: OsStr ;
2
+ use super :: mystd:: fs;
3
+ use super :: mystd:: os:: unix:: ffi:: OsStrExt ;
4
+ use super :: mystd:: path:: { Path , PathBuf } ;
5
+ use super :: { Context , Mapping , Stash , Vec } ;
6
+ use core:: convert:: { TryFrom , TryInto } ;
7
+ use core:: str;
8
+ use object:: elf:: { ELFCOMPRESS_ZLIB , ELF_NOTE_GNU , NT_GNU_BUILD_ID , SHF_COMPRESSED } ;
4
9
use object:: read:: elf:: { CompressionHeader , FileHeader , SectionHeader , SectionTable , Sym } ;
5
10
use object:: read:: StringTable ;
6
11
use object:: { BigEndian , Bytes , NativeEndian } ;
@@ -10,11 +15,118 @@ type Elf = object::elf::FileHeader32<NativeEndian>;
10
15
#[ cfg( target_pointer_width = "64" ) ]
11
16
type Elf = object:: elf:: FileHeader64 < NativeEndian > ;
12
17
18
+ const BUILD_ID_PATH : & [ u8 ] = b"/usr/lib/debug/.build-id/" ;
19
+ const BUILD_ID_SUFFIX : & [ u8 ] = b".debug" ;
20
+
13
21
impl Mapping {
14
22
pub fn new ( path : & Path ) -> Option < Mapping > {
15
23
let map = super :: mmap ( path) ?;
16
- Mapping :: mk ( map, |data, stash| Context :: new ( stash, Object :: parse ( data) ?) )
24
+ let object = Object :: parse ( & map) ?;
25
+
26
+ // Try to locate an external debug file using the build ID.
27
+ let build_id = object. build_id ( ) . unwrap_or ( & [ ] ) ;
28
+ if !build_id. is_empty ( ) {
29
+ fn hex ( byte : u8 ) -> u8 {
30
+ if byte < 10 {
31
+ b'0' + byte
32
+ } else {
33
+ b'a' + byte - 10
34
+ }
35
+ }
36
+ let mut path = Vec :: with_capacity (
37
+ BUILD_ID_PATH . len ( ) + BUILD_ID_SUFFIX . len ( ) + build_id. len ( ) * 2 + 1 ,
38
+ ) ;
39
+ path. extend ( BUILD_ID_PATH ) ;
40
+ path. push ( hex ( build_id[ 0 ] >> 4 ) ) ;
41
+ path. push ( hex ( build_id[ 0 ] & 0xf ) ) ;
42
+ path. push ( b'/' ) ;
43
+ for byte in & build_id[ 1 ..] {
44
+ path. push ( hex ( byte >> 4 ) ) ;
45
+ path. push ( hex ( byte & 0xf ) ) ;
46
+ }
47
+ path. extend ( BUILD_ID_SUFFIX ) ;
48
+ if let Some ( mapping) = Mapping :: new_debug ( Path :: new ( OsStr :: from_bytes ( & path) ) , None ) {
49
+ return Some ( mapping) ;
50
+ }
51
+ }
52
+
53
+ // Try to locate an external debug file using the GNU debug link section.
54
+ let tmp_stash = Stash :: new ( ) ;
55
+ if let Some ( section) = object. section ( & tmp_stash, ".gnu_debuglink" ) {
56
+ if let Some ( len) = section. iter ( ) . position ( |x| * x == 0 ) {
57
+ let filename = & section[ ..len] ;
58
+ let offset = ( len + 1 + 3 ) & !3 ;
59
+ if let Some ( crc_bytes) = section
60
+ . get ( offset..offset + 4 )
61
+ . and_then ( |bytes| bytes. try_into ( ) . ok ( ) )
62
+ {
63
+ let crc = u32:: from_ne_bytes ( crc_bytes) ;
64
+ if let Some ( path_debug) = locate_debuglink ( path, filename) {
65
+ if let Some ( mapping) = Mapping :: new_debug ( & path_debug, Some ( crc) ) {
66
+ return Some ( mapping) ;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ let stash = Stash :: new ( ) ;
74
+ let cx = Context :: new ( & stash, object) ?;
75
+ Some ( Mapping {
76
+ // Convert to 'static lifetimes since the symbols should
77
+ // only borrow `map` and `stash` and we're preserving them below.
78
+ cx : unsafe { core:: mem:: transmute :: < Context < ' _ > , Context < ' static > > ( cx) } ,
79
+ _map : map,
80
+ _stash : stash,
81
+ } )
82
+ }
83
+
84
+ /// Load debuginfo from an external debug file.
85
+ fn new_debug ( path : & Path , crc : Option < u32 > ) -> Option < Mapping > {
86
+ let map = super :: mmap ( path) ?;
87
+ let object = Object :: parse ( & map) ?;
88
+
89
+ if let Some ( _crc) = crc {
90
+ // TODO: check crc
91
+ }
92
+
93
+ let stash = Stash :: new ( ) ;
94
+ let cx = Context :: new ( & stash, object) ?;
95
+ Some ( Mapping {
96
+ // Convert to 'static lifetimes since the symbols should
97
+ // only borrow `map` and `stash` and we're preserving them below.
98
+ cx : unsafe { core:: mem:: transmute :: < Context < ' _ > , Context < ' static > > ( cx) } ,
99
+ _map : map,
100
+ _stash : stash,
101
+ } )
102
+ }
103
+ }
104
+
105
+ fn locate_debuglink ( path : & Path , filename : & [ u8 ] ) -> Option < PathBuf > {
106
+ let path = fs:: canonicalize ( path) . ok ( ) ?;
107
+ let parent = path. parent ( ) ?;
108
+ let filename = Path :: new ( OsStr :: from_bytes ( filename) ) ;
109
+
110
+ // Try "/parent/filename" if it differs from "path"
111
+ let f = parent. join ( filename) ;
112
+ if f != path && f. is_file ( ) {
113
+ return Some ( f) ;
114
+ }
115
+
116
+ // Try "/parent/.debug/filename"
117
+ let f = parent. join ( ".debug" ) . join ( filename) ;
118
+ if f. is_file ( ) {
119
+ return Some ( f) ;
17
120
}
121
+
122
+ // Try "/usr/lib/debug/parent/filename"
123
+ let parent = parent. strip_prefix ( "/" ) . unwrap ( ) ;
124
+ let f = Path :: new ( "/usr/lib/debug" ) . join ( parent) . join ( filename) ;
125
+ if f. is_file ( ) {
126
+ return Some ( f) ;
127
+ }
128
+
129
+ None
18
130
}
19
131
20
132
struct ParsedSym {
@@ -163,6 +275,19 @@ impl<'a> Object<'a> {
163
275
pub ( super ) fn search_object_map ( & self , _addr : u64 ) -> Option < ( & Context < ' _ > , u64 ) > {
164
276
None
165
277
}
278
+
279
+ pub ( super ) fn build_id ( & self ) -> Option < & ' a [ u8 ] > {
280
+ for section in self . sections . iter ( ) {
281
+ if let Ok ( Some ( mut notes) ) = section. notes ( self . endian , self . data ) {
282
+ while let Ok ( Some ( note) ) = notes. next ( ) {
283
+ if note. name ( ) == ELF_NOTE_GNU && note. n_type ( self . endian ) == NT_GNU_BUILD_ID {
284
+ return Some ( note. desc ( ) ) ;
285
+ }
286
+ }
287
+ }
288
+ }
289
+ None
290
+ }
166
291
}
167
292
168
293
fn decompress_zlib ( input : & [ u8 ] , output : & mut [ u8 ] ) -> Option < ( ) > {
0 commit comments