1
- use super :: { Context , Mapping , Mmap , Path , Stash , Vec } ;
1
+ use super :: { Box , Context , Mapping , Mmap , Path , Stash , Vec } ;
2
2
use core:: convert:: TryInto ;
3
3
use object:: macho;
4
4
use object:: read:: macho:: { MachHeader , Nlist , Section , Segment as _} ;
@@ -137,21 +137,32 @@ fn find_header(mut data: Bytes<'_>) -> Option<(&'_ Mach, Bytes<'_>)> {
137
137
Mach :: parse ( data) . ok ( ) . map ( |h| ( h, data) )
138
138
}
139
139
140
+ // This is used both for executables/libraries and source object files.
140
141
pub struct Object < ' a > {
141
142
endian : NativeEndian ,
142
143
data : Bytes < ' a > ,
143
144
dwarf : Option < & ' a [ MachSection ] > ,
144
145
syms : Vec < ( & ' a [ u8 ] , u64 ) > ,
146
+ syms_sort_by_name : bool ,
147
+ // Only set for executables/libraries, and not the source object files.
148
+ object_map : Option < object:: ObjectMap < ' a > > ,
149
+ // The outer Option is for lazy loading, and the inner Option allows load errors to be cached.
150
+ object_mappings : Box < [ Option < Option < Mapping > > ] > ,
145
151
}
146
152
147
153
impl < ' a > Object < ' a > {
148
154
fn parse ( mach : & ' a Mach , endian : NativeEndian , data : Bytes < ' a > ) -> Option < Object < ' a > > {
155
+ let is_object = mach. filetype ( endian) == object:: macho:: MH_OBJECT ;
149
156
let mut dwarf = None ;
150
157
let mut syms = Vec :: new ( ) ;
158
+ let mut syms_sort_by_name = false ;
151
159
let mut commands = mach. load_commands ( endian, data) . ok ( ) ?;
160
+ let mut object_map = None ;
161
+ let mut object_mappings = Vec :: new ( ) ;
152
162
while let Ok ( Some ( command) ) = commands. next ( ) {
153
163
if let Some ( ( segment, section_data) ) = MachSegment :: from_command ( command) . ok ( ) ? {
154
- if segment. name ( ) == b"__DWARF" {
164
+ // Object files should have all sections in a single unnamed segment load command.
165
+ if segment. name ( ) == b"__DWARF" || ( is_object && segment. name ( ) == b"" ) {
155
166
dwarf = segment. sections ( endian, section_data) . ok ( ) ;
156
167
}
157
168
} else if let Some ( symtab) = command. symtab ( ) . ok ( ) ? {
@@ -167,7 +178,18 @@ impl<'a> Object<'a> {
167
178
}
168
179
} )
169
180
. collect ( ) ;
170
- syms. sort_unstable_by_key ( |( _, addr) | * addr) ;
181
+ if is_object {
182
+ // We never search object file symbols by address.
183
+ // Instead, we already know the symbol name from the executable, and we
184
+ // need to search by name to find the matching symbol in the object file.
185
+ syms. sort_unstable_by_key ( |( name, _) | * name) ;
186
+ syms_sort_by_name = true ;
187
+ } else {
188
+ syms. sort_unstable_by_key ( |( _, addr) | * addr) ;
189
+ let map = symbols. object_map ( endian) ;
190
+ object_mappings. resize_with ( map. objects ( ) . len ( ) , || None ) ;
191
+ object_map = Some ( map) ;
192
+ }
171
193
}
172
194
}
173
195
@@ -176,6 +198,9 @@ impl<'a> Object<'a> {
176
198
data,
177
199
dwarf,
178
200
syms,
201
+ syms_sort_by_name,
202
+ object_map,
203
+ object_mappings : object_mappings. into_boxed_slice ( ) ,
179
204
} )
180
205
}
181
206
@@ -194,11 +219,83 @@ impl<'a> Object<'a> {
194
219
}
195
220
196
221
pub fn search_symtab < ' b > ( & ' b self , addr : u64 ) -> Option < & ' b [ u8 ] > {
222
+ debug_assert ! ( !self . syms_sort_by_name) ;
197
223
let i = match self . syms . binary_search_by_key ( & addr, |( _, addr) | * addr) {
198
224
Ok ( i) => i,
199
225
Err ( i) => i. checked_sub ( 1 ) ?,
200
226
} ;
201
227
let ( sym, _addr) = self . syms . get ( i) ?;
202
228
Some ( sym)
203
229
}
230
+
231
+ /// Try to load a context for an object file.
232
+ ///
233
+ /// If dsymutil was not run, then the DWARF may be found in the source object files.
234
+ pub ( super ) fn search_object_map < ' b > ( & ' b mut self , addr : u64 ) -> Option < ( & Context < ' b > , u64 ) > {
235
+ // `object_map` contains a map from addresses to symbols and object paths.
236
+ // Look up the address and get a mapping for the object.
237
+ let object_map = self . object_map . as_ref ( ) ?;
238
+ let symbol = object_map. get ( addr) ?;
239
+ let object_index = symbol. object_index ( ) ;
240
+ let mapping = self . object_mappings . get_mut ( object_index) ?;
241
+ if mapping. is_none ( ) {
242
+ // No cached mapping, so create it.
243
+ * mapping = Some ( object_mapping ( object_map. objects ( ) . get ( object_index) ?) ) ;
244
+ }
245
+ let cx: & ' b Context < ' static > = & mapping. as_ref ( ) ?. as_ref ( ) ?. cx ;
246
+ // Don't leak the `'static` lifetime, make sure it's scoped to just ourselves.
247
+ let cx = unsafe { core:: mem:: transmute :: < & ' b Context < ' static > , & ' b Context < ' b > > ( cx) } ;
248
+
249
+ // We must translate the address in order to be able to look it up
250
+ // in the DWARF in the object file.
251
+ debug_assert ! ( cx. object. syms. is_empty( ) || cx. object. syms_sort_by_name) ;
252
+ let i = cx
253
+ . object
254
+ . syms
255
+ . binary_search_by_key ( & symbol. name ( ) , |( name, _) | * name)
256
+ . ok ( ) ?;
257
+ let object_symbol = cx. object . syms . get ( i) ?;
258
+ let object_addr = addr
259
+ . wrapping_sub ( symbol. address ( ) )
260
+ . wrapping_add ( object_symbol. 1 ) ;
261
+ Some ( ( cx, object_addr) )
262
+ }
263
+ }
264
+
265
+ fn object_mapping ( path : & [ u8 ] ) -> Option < Mapping > {
266
+ use super :: mystd:: ffi:: OsStr ;
267
+ use super :: mystd:: os:: unix:: prelude:: * ;
268
+
269
+ let map;
270
+
271
+ // `N_OSO` symbol names can be either `/path/to/object.o` or `/path/to/archive.a(object.o)`.
272
+ let data = if let Some ( ( archive_path, member_name) ) = split_archive_path ( path) {
273
+ map = super :: mmap ( Path :: new ( OsStr :: from_bytes ( archive_path) ) ) ?;
274
+ let archive = object:: read:: archive:: ArchiveFile :: parse ( & map) . ok ( ) ?;
275
+ let member = archive
276
+ . members ( )
277
+ . filter_map ( Result :: ok)
278
+ . find ( |m| m. name ( ) == member_name) ?;
279
+ Bytes ( member. data ( ) )
280
+ } else {
281
+ map = super :: mmap ( Path :: new ( OsStr :: from_bytes ( path) ) ) ?;
282
+ Bytes ( & map)
283
+ } ;
284
+
285
+ let ( macho, data) = find_header ( data) ?;
286
+ let endian = macho. endian ( ) . ok ( ) ?;
287
+ let object = Object :: parse ( macho, endian, data) ?;
288
+ let stash = Stash :: new ( ) ;
289
+ let inner = super :: cx ( & stash, object) ?;
290
+ Some ( mk ! ( Mapping { map, inner, stash } ) )
291
+ }
292
+
293
+ fn split_archive_path ( path : & [ u8 ] ) -> Option < ( & [ u8 ] , & [ u8 ] ) > {
294
+ let ( last, path) = path. split_last ( ) ?;
295
+ if * last != b')' {
296
+ return None ;
297
+ }
298
+ let index = path. iter ( ) . position ( |& x| x == b'(' ) ?;
299
+ let ( archive, rest) = path. split_at ( index) ;
300
+ Some ( ( archive, & rest[ 1 ..] ) )
204
301
}
0 commit comments