samply_symbols/lib.rs
1//! This crate allows obtaining symbol information from binaries and compilation artifacts.
2//!
3//! You probably want to be using the [`wholesym` crate](https://docs.rs/wholesym/) instead.
4//! `wholesym` has a much more ergonomic API; it is a wrapper around `samply-symbols`.
5//!
6//! More specifically, `samply-symbols` provides the low-level implementation of `wholesym`,
7//! while satisfying both native and WebAssembly consumers, whereas `wholesym` only cares about
8//! native consumers.
9//!
10//! The main entry point of this crate is the `SymbolManager` struct and its async `load_symbol_map` method.
11//! With a `SymbolMap`, you can resolve raw code addresses to function name strings, and, if available,
12//! to file name + line number information and inline stacks.
13//!
14//! # Design constraints
15//!
16//! This crate operates under the following design constraints:
17//!
18//! - Must be usable from JavaScript / WebAssembly: The Firefox profiler runs this code in a
19//! WebAssembly environment, invoked from a privileged piece of JavaScript code inside Firefox itself.
20//! This setup allows us to download a wasm bundle on demand, rather than shipping
21//! it with Firefox, which would increase the Firefox download size for a piece of functionality
22//! that the vast majority of Firefox users don't need.
23//! - Performance: We want to be able to obtain symbol data from a fresh build of a locally compiled
24//! Firefox instance as quickly as possible, without an expensive preprocessing step. The time between
25//! "finished compilation" and "returned symbol data" should be minimized. This means that symbol
26//! data needs to be obtained directly from the compilation artifacts rather than from, say, a
27//! dSYM bundle or a Breakpad .sym file.
28//! - Must scale to large inputs: This applies to both the size of the API request and the size of the
29//! object files that need to be parsed: The Firefox profiler will supply anywhere between tens of
30//! thousands and hundreds of thousands of different code addresses in a single symbolication request.
31//! Firefox build artifacts such as libxul.so can be multiple gigabytes big, and contain around 300000
32//! function symbols. We want to serve such requests within a few seconds or less.
33//! - "Best effort" basis: If only limited symbol information is available, for example from system
34//! libraries, we want to return whatever limited information we have.
35//!
36//! The WebAssembly requirement means that this crate cannot contain any direct file access.
37//! Instead, all file access is mediated through a `FileAndPathHelper` trait which has to be implemented
38//! by the caller. We cannot even use the `std::path::Path` / `PathBuf` types to represent paths,
39//! because the WASM bundle can run on Windows, and the `Path` / `PathBuf` types have! Unix path
40//! semantics in Rust-compiled-to-WebAssembly.
41//!
42//! Furthermore, the caller needs to be able to find the right symbol files based on a subset
43//! of information about a library, for example just based on its debug name and debug ID. This
44//! is used when `SymbolManager::load_symbol_map` is called with such a subset of information.
45//! More concretely, this ability is used by `samply-api` when processing a JSON symbolication
46//! API call, which only comes with the debug name and debug ID for a library.
47//!
48//! # Supported formats and data
49//!
50//! This crate supports obtaining symbol data from PE binaries (Windows), PDB files (Windows),
51//! mach-o binaries (including fat binaries) (macOS & iOS), and ELF binaries (Linux, Android, etc.).
52//! For mach-o files it also supports finding debug information in external objects, by following
53//! OSO stabs entries.
54//! It supports gathering both basic symbol information (function name strings) as well as information
55//! based on debug data, i.e. inline callstacks where each frame has a function name, a file name,
56//! and a line number.
57//! For debug data we support both DWARF debug data (inside mach-o and ELF binaries) and PDB debug data.
58//!
59//! # Example
60//!
61//! ```rust
62//! use samply_symbols::debugid::DebugId;
63//! use samply_symbols::{
64//! CandidatePathInfo, FileAndPathHelper, FileAndPathHelperResult, FileLocation,
65//! FramesLookupResult, LibraryInfo, LookupAddress, OptionallySendFuture, SymbolManager,
66//! };
67//!
68//! async fn run_query() {
69//! let this_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
70//! let helper = ExampleHelper {
71//! artifact_directory: this_dir.join("..").join("fixtures").join("win64-ci"),
72//! };
73//!
74//! let symbol_manager = SymbolManager::with_helper(helper);
75//!
76//! let library_info = LibraryInfo {
77//! debug_name: Some("firefox.pdb".to_string()),
78//! debug_id: DebugId::from_breakpad("AA152DEB2D9B76084C4C44205044422E1").ok(),
79//! ..Default::default()
80//! };
81//! let symbol_map = match symbol_manager.load_symbol_map(&library_info).await {
82//! Ok(symbol_map) => symbol_map,
83//! Err(e) => {
84//! println!("Error while loading the symbol map: {:?}", e);
85//! return;
86//! }
87//! };
88//!
89//! // Look up the symbol for an address.
90//! let lookup_result = symbol_map.lookup(LookupAddress::Relative(0x1f98f)).await;
91//!
92//! match lookup_result {
93//! Some(address_info) => {
94//! // Print the symbol name for this address:
95//! println!("0x1f98f: {}", address_info.symbol.name);
96//!
97//! // See if we have debug info (file name + line, and inlined frames):
98//! if let Some(frames) = address_info.frames {
99//! println!("Debug info:");
100//! for frame in frames {
101//! println!(
102//! " - {:?} ({:?}:{:?})",
103//! frame.function, frame.file_path, frame.line_number
104//! );
105//! }
106//! }
107//! }
108//! None => {
109//! println!("No symbol was found for address 0x1f98f.")
110//! }
111//! }
112//! }
113//!
114//! struct ExampleHelper {
115//! artifact_directory: std::path::PathBuf,
116//! }
117//!
118//! impl FileAndPathHelper for ExampleHelper {
119//! type F = Vec<u8>;
120//! type FL = ExampleFileLocation;
121//!
122//! fn get_candidate_paths_for_debug_file(
123//! &self,
124//! library_info: &LibraryInfo,
125//! ) -> FileAndPathHelperResult<Vec<CandidatePathInfo<ExampleFileLocation>>> {
126//! if let Some(debug_name) = library_info.debug_name.as_deref() {
127//! Ok(vec![CandidatePathInfo::SingleFile(ExampleFileLocation(
128//! self.artifact_directory.join(debug_name),
129//! ))])
130//! } else {
131//! Ok(vec![])
132//! }
133//! }
134//!
135//! fn get_candidate_paths_for_binary(
136//! &self,
137//! library_info: &LibraryInfo,
138//! ) -> FileAndPathHelperResult<Vec<CandidatePathInfo<ExampleFileLocation>>> {
139//! if let Some(name) = library_info.name.as_deref() {
140//! Ok(vec![CandidatePathInfo::SingleFile(ExampleFileLocation(
141//! self.artifact_directory.join(name),
142//! ))])
143//! } else {
144//! Ok(vec![])
145//! }
146//! }
147//!
148//! fn get_dyld_shared_cache_paths(
149//! &self,
150//! _arch: Option<&str>,
151//! ) -> FileAndPathHelperResult<Vec<ExampleFileLocation>> {
152//! Ok(vec![])
153//! }
154//!
155//! fn load_file(
156//! &self,
157//! location: ExampleFileLocation,
158//! ) -> std::pin::Pin<Box<dyn OptionallySendFuture<Output = FileAndPathHelperResult<Self::F>> + '_>> {
159//! Box::pin(async move { Ok(std::fs::read(&location.0)?) })
160//! }
161//! }
162//!
163//! #[derive(Clone, Debug)]
164//! struct ExampleFileLocation(std::path::PathBuf);
165//!
166//! impl std::fmt::Display for ExampleFileLocation {
167//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168//! self.0.to_string_lossy().fmt(f)
169//! }
170//! }
171//!
172//! impl FileLocation for ExampleFileLocation {
173//! fn location_for_dyld_subcache(&self, suffix: &str) -> Option<Self> {
174//! let mut filename = self.0.file_name().unwrap().to_owned();
175//! filename.push(suffix);
176//! Some(Self(self.0.with_file_name(filename)))
177//! }
178//!
179//! fn location_for_external_object_file(&self, object_file: &str) -> Option<Self> {
180//! Some(Self(object_file.into()))
181//! }
182//!
183//! fn location_for_pdb_from_binary(&self, pdb_path_in_binary: &str) -> Option<Self> {
184//! Some(Self(pdb_path_in_binary.into()))
185//! }
186//!
187//! fn location_for_source_file(&self, source_file_path: &str) -> Option<Self> {
188//! Some(Self(source_file_path.into()))
189//! }
190//!
191//! fn location_for_breakpad_symindex(&self) -> Option<Self> {
192//! Some(Self(self.0.with_extension("symindex")))
193//! }
194//!
195//! fn location_for_dwo(&self, _comp_dir: &str, path: &str) -> Option<Self> {
196//! Some(Self(path.into()))
197//! }
198//!
199//! fn location_for_dwp(&self) -> Option<Self> {
200//! let mut s = self.0.as_os_str().to_os_string();
201//! s.push(".dwp");
202//! Some(Self(s.into()))
203//! }
204//! }
205//! ```
206
207use std::sync::Arc;
208
209use binary_image::BinaryImageInner;
210use jitdump::JitDumpIndex;
211use linux_perf_data::jitdump::JitDumpReader;
212use object::read::FileKind;
213pub use pdb_addr2line::pdb;
214use shared::FileContentsCursor;
215pub use {debugid, object};
216
217mod binary_image;
218mod breakpad;
219mod cache;
220mod chunked_read_buffer_manager;
221mod compact_symbol_table;
222mod debugid_util;
223mod demangle;
224mod demangle_ocaml;
225mod dwarf;
226mod elf;
227mod error;
228mod external_file;
229mod jitdump;
230mod macho;
231mod mapped_path;
232mod path_mapper;
233mod shared;
234mod symbol_map;
235mod symbol_map_object;
236mod windows;
237
238pub use crate::binary_image::{BinaryImage, CodeByteReadingError};
239pub use crate::breakpad::{
240 BreakpadIndex, BreakpadIndexParser, BreakpadParseError, BreakpadSymindexParseError,
241};
242pub use crate::cache::{FileByteSource, FileContentsWithChunkedCaching};
243pub use crate::compact_symbol_table::CompactSymbolTable;
244pub use crate::debugid_util::{debug_id_for_object, DebugIdExt};
245pub use crate::demangle::demangle_any;
246pub use crate::error::Error;
247pub use crate::external_file::{load_external_file, ExternalFileSymbolMap};
248pub use crate::jitdump::debug_id_and_code_id_for_jitdump;
249pub use crate::macho::FatArchiveMember;
250pub use crate::mapped_path::MappedPath;
251pub use crate::shared::{
252 relative_address_base, AddressInfo, CandidatePathInfo, CodeId, ElfBuildId,
253 ExternalFileAddressInFileRef, ExternalFileAddressRef, ExternalFileRef, FileAndPathHelper,
254 FileAndPathHelperError, FileAndPathHelperResult, FileContents, FileContentsWrapper,
255 FileLocation, FrameDebugInfo, FramesLookupResult, LibraryInfo, LookupAddress,
256 MultiArchDisambiguator, OptionallySendFuture, PeCodeId, SourceFilePath, SymbolInfo,
257 SyncAddressInfo,
258};
259pub use crate::symbol_map::{SymbolMap, SymbolMapTrait};
260
261pub struct SymbolManager<H: FileAndPathHelper> {
262 helper: Arc<H>,
263}
264
265impl<H, F, FL> SymbolManager<H>
266where
267 H: FileAndPathHelper<F = F, FL = FL>,
268 F: FileContents + 'static,
269 FL: FileLocation,
270{
271 // Create a new `SymbolManager`.
272 pub fn with_helper(helper: H) -> Self {
273 Self {
274 helper: Arc::new(helper),
275 }
276 }
277
278 /// Exposes the helper.
279 pub fn helper(&self) -> Arc<H> {
280 self.helper.clone()
281 }
282
283 pub async fn load_source_file(
284 &self,
285 debug_file_location: &H::FL,
286 source_file_path: &SourceFilePath,
287 ) -> Result<String, Error> {
288 let source_file_location = debug_file_location
289 .location_for_source_file(source_file_path.raw_path())
290 .ok_or(Error::FileLocationRefusedSourceFileLocation)?;
291 let file_contents = self
292 .helper
293 .load_file(source_file_location.clone())
294 .await
295 .map_err(|e| Error::HelperErrorDuringOpenFile(source_file_location.to_string(), e))?;
296 let file_contents = file_contents
297 .read_bytes_at(0, file_contents.len())
298 .map_err(|e| {
299 Error::HelperErrorDuringFileReading(source_file_location.to_string(), e)
300 })?;
301 Ok(String::from_utf8_lossy(file_contents).to_string())
302 }
303
304 /// Obtain a symbol map for the library, given the (partial) `LibraryInfo`.
305 /// At least the debug_id has to be given.
306 pub async fn load_symbol_map(&self, library_info: &LibraryInfo) -> Result<SymbolMap<H>, Error> {
307 if let Some((fl, symbol_map)) = self
308 .helper()
309 .as_ref()
310 .get_symbol_map_for_library(library_info)
311 {
312 return Ok(SymbolMap::with_symbol_map_trait(fl, symbol_map));
313 }
314
315 let debug_id = match library_info.debug_id {
316 Some(debug_id) => debug_id,
317 None => return Err(Error::NotEnoughInformationToIdentifySymbolMap),
318 };
319
320 let candidate_paths = self
321 .helper
322 .get_candidate_paths_for_debug_file(library_info)
323 .map_err(|e| {
324 Error::HelperErrorDuringGetCandidatePathsForDebugFile(
325 Box::new(library_info.clone()),
326 e,
327 )
328 })?;
329
330 let mut all_errors = Vec::new();
331 for candidate_info in candidate_paths {
332 let symbol_map = match candidate_info {
333 CandidatePathInfo::SingleFile(file_location) => {
334 self.load_symbol_map_from_location(
335 file_location,
336 Some(MultiArchDisambiguator::DebugId(debug_id)),
337 )
338 .await
339 }
340 CandidatePathInfo::InDyldCache {
341 dyld_cache_path,
342 dylib_path,
343 } => {
344 macho::load_symbol_map_for_dyld_cache(
345 dyld_cache_path,
346 dylib_path,
347 &*self.helper,
348 )
349 .await
350 }
351 };
352
353 match symbol_map {
354 Ok(symbol_map) if symbol_map.debug_id() == debug_id => return Ok(symbol_map),
355 Ok(symbol_map) => {
356 all_errors.push(Error::UnmatchedDebugId(symbol_map.debug_id(), debug_id));
357 }
358 Err(e) => {
359 all_errors.push(e);
360 }
361 }
362 }
363 let err = match all_errors.len() {
364 0 => Error::NoCandidatePathForDebugFile(Box::new(library_info.clone())),
365 1 => all_errors.pop().unwrap(),
366 _ => Error::NoSuccessfulCandidate(all_errors),
367 };
368 Err(err)
369 }
370
371 /// Load and return an external file which may contain additional debug info.
372 ///
373 /// This is used on macOS: When linking multiple `.o` files together into a library or
374 /// an executable, the linker does not copy the dwarf sections into the linked output.
375 /// Instead, it stores the paths to those original `.o` files, using OSO stabs entries.
376 ///
377 /// A `SymbolMap` for such a linked file will not find debug info, and will return
378 /// `FramesLookupResult::External` from the lookups. Then the address needs to be
379 /// looked up in the external file.
380 ///
381 /// Also see `SymbolMap::lookup_external`.
382 pub async fn load_external_file(
383 &self,
384 debug_file_location: &H::FL,
385 external_file_path: &str,
386 ) -> Result<ExternalFileSymbolMap<H::F>, Error> {
387 let external_file_location = debug_file_location
388 .location_for_external_object_file(external_file_path)
389 .ok_or(Error::FileLocationRefusedExternalObjectLocation)?;
390 external_file::load_external_file(&*self.helper, external_file_location, external_file_path)
391 .await
392 }
393
394 async fn load_binary_from_dyld_cache(
395 &self,
396 dyld_cache_path: FL,
397 dylib_path: String,
398 ) -> Result<BinaryImage<F>, Error> {
399 macho::load_binary_from_dyld_cache(dyld_cache_path, dylib_path, &*self.helper).await
400 }
401
402 /// Returns the binary for the given (partial) [`LibraryInfo`].
403 ///
404 /// This consults the helper to get candidate paths to the binary.
405 pub async fn load_binary(&self, info: &LibraryInfo) -> Result<BinaryImage<F>, Error> {
406 // Require at least either the code ID or a (debug_name, debug_id) pair.
407 if info.code_id.is_none() && (info.debug_name.is_none() || info.debug_id.is_none()) {
408 return Err(Error::NotEnoughInformationToIdentifyBinary);
409 }
410
411 let candidate_paths_for_binary = self
412 .helper
413 .get_candidate_paths_for_binary(info)
414 .map_err(Error::HelperErrorDuringGetCandidatePathsForBinary)?;
415
416 let disambiguator = match (&info.debug_id, &info.arch) {
417 (Some(debug_id), _) => Some(MultiArchDisambiguator::DebugId(*debug_id)),
418 (None, Some(arch)) => Some(MultiArchDisambiguator::Arch(arch.clone())),
419 (None, None) => None,
420 };
421
422 let mut last_err = None;
423 for candidate_info in candidate_paths_for_binary {
424 let image = match candidate_info {
425 CandidatePathInfo::SingleFile(file_location) => {
426 self.load_binary_at_location(
427 file_location,
428 info.name.clone(),
429 None,
430 disambiguator.clone(),
431 )
432 .await
433 }
434 CandidatePathInfo::InDyldCache {
435 dyld_cache_path,
436 dylib_path,
437 } => {
438 self.load_binary_from_dyld_cache(dyld_cache_path, dylib_path)
439 .await
440 }
441 };
442
443 match image {
444 Ok(image) => {
445 let e = if let Some(expected_debug_id) = info.debug_id {
446 if image.debug_id() == Some(expected_debug_id) {
447 return Ok(image);
448 }
449 Error::UnmatchedDebugIdOptional(expected_debug_id, image.debug_id())
450 } else if let Some(expected_code_id) = info.code_id.as_ref() {
451 if image.code_id().as_ref() == Some(expected_code_id) {
452 return Ok(image);
453 }
454 Error::UnmatchedCodeId(expected_code_id.clone(), image.code_id())
455 } else {
456 panic!(
457 "We checked earlier that we have at least one of debug_id / code_id."
458 )
459 };
460 last_err = Some(e);
461 }
462 Err(e) => {
463 last_err = Some(e);
464 }
465 }
466 }
467 Err(last_err.unwrap_or_else(|| {
468 Error::NoCandidatePathForBinary(info.debug_name.clone(), info.debug_id)
469 }))
470 }
471
472 pub async fn load_binary_for_dyld_cache_image(
473 &self,
474 dylib_path: &str,
475 multi_arch_disambiguator: Option<MultiArchDisambiguator>,
476 ) -> Result<BinaryImage<F>, Error> {
477 let arch = match &multi_arch_disambiguator {
478 Some(MultiArchDisambiguator::Arch(arch)) => Some(arch.as_str()),
479 _ => None,
480 };
481 let dyld_shared_cache_paths = self
482 .helper
483 .get_dyld_shared_cache_paths(arch)
484 .map_err(Error::HelperErrorDuringGetDyldSharedCachePaths)?;
485
486 let mut err = None;
487 for dyld_cache_path in dyld_shared_cache_paths {
488 let binary_res = self
489 .load_binary_from_dyld_cache(dyld_cache_path, dylib_path.to_owned())
490 .await;
491 match (&multi_arch_disambiguator, binary_res) {
492 (Some(MultiArchDisambiguator::DebugId(expected_debug_id)), Ok(binary)) => {
493 if binary.debug_id().as_ref() == Some(expected_debug_id) {
494 return Ok(binary);
495 }
496 err = Some(Error::UnmatchedDebugIdOptional(
497 *expected_debug_id,
498 binary.debug_id(),
499 ));
500 }
501 (_, Ok(binary)) => return Ok(binary),
502 (_, Err(e)) => err = Some(e),
503 }
504 }
505 Err(err.unwrap_or(Error::NoCandidatePathForDyldCache))
506 }
507
508 pub async fn load_symbol_map_for_dyld_cache_image(
509 &self,
510 dylib_path: &str,
511 multi_arch_disambiguator: Option<MultiArchDisambiguator>,
512 ) -> Result<SymbolMap<H>, Error> {
513 let arch = match &multi_arch_disambiguator {
514 Some(MultiArchDisambiguator::Arch(arch)) => Some(arch.as_str()),
515 _ => None,
516 };
517 let dyld_shared_cache_paths = self
518 .helper
519 .get_dyld_shared_cache_paths(arch)
520 .map_err(Error::HelperErrorDuringGetDyldSharedCachePaths)?;
521
522 let mut err = None;
523 for dyld_cache_path in dyld_shared_cache_paths {
524 let symbol_map_res = macho::load_symbol_map_for_dyld_cache(
525 dyld_cache_path,
526 dylib_path.to_owned(),
527 &*self.helper,
528 )
529 .await;
530 match (&multi_arch_disambiguator, symbol_map_res) {
531 (Some(MultiArchDisambiguator::DebugId(expected_debug_id)), Ok(symbol_map)) => {
532 if &symbol_map.debug_id() == expected_debug_id {
533 return Ok(symbol_map);
534 }
535 err = Some(Error::UnmatchedDebugId(
536 symbol_map.debug_id(),
537 *expected_debug_id,
538 ));
539 }
540 (_, Ok(symbol_map)) => return Ok(symbol_map),
541 (_, Err(e)) => err = Some(e),
542 }
543 }
544 Err(err.unwrap_or(Error::NoCandidatePathForDyldCache))
545 }
546
547 pub async fn load_symbol_map_from_location(
548 &self,
549 file_location: FL,
550 multi_arch_disambiguator: Option<MultiArchDisambiguator>,
551 ) -> Result<SymbolMap<H>, Error> {
552 let file_contents = self
553 .helper
554 .load_file(file_location.clone())
555 .await
556 .map_err(|e| Error::HelperErrorDuringOpenFile(file_location.to_string(), e))?;
557
558 let file_contents = FileContentsWrapper::new(file_contents);
559
560 if let Ok(file_kind) = FileKind::parse(&file_contents) {
561 match file_kind {
562 FileKind::Elf32 | FileKind::Elf64 => {
563 elf::load_symbol_map_for_elf(
564 file_location,
565 file_contents,
566 file_kind,
567 self.helper(),
568 )
569 .await
570 }
571 FileKind::MachOFat32 | FileKind::MachOFat64 => {
572 let member = macho::get_fat_archive_member(
573 &file_contents,
574 file_kind,
575 multi_arch_disambiguator,
576 )?;
577 macho::get_symbol_map_for_fat_archive_member(
578 file_location,
579 file_contents,
580 member,
581 self.helper(),
582 )
583 }
584 FileKind::MachO32 | FileKind::MachO64 => {
585 macho::get_symbol_map_for_macho(file_location, file_contents, self.helper())
586 }
587 FileKind::Pe32 | FileKind::Pe64 => {
588 match windows::load_symbol_map_for_pdb_corresponding_to_binary(
589 file_kind,
590 &file_contents,
591 file_location.clone(),
592 &*self.helper,
593 )
594 .await
595 {
596 Ok(symbol_map) => Ok(symbol_map),
597 Err(_) => windows::get_symbol_map_for_pe(
598 file_contents,
599 file_kind,
600 file_location,
601 self.helper(),
602 ),
603 }
604 }
605 _ => Err(Error::InvalidInputError(
606 "Input was Archive, Coff or Wasm format, which are unsupported for now",
607 )),
608 }
609 } else if windows::is_pdb_file(&file_contents) {
610 windows::get_symbol_map_for_pdb(file_contents, file_location)
611 } else if breakpad::is_breakpad_file(&file_contents) {
612 let index_file_contents =
613 if let Some(index_file_location) = file_location.location_for_breakpad_symindex() {
614 self.helper
615 .load_file(index_file_location)
616 .await
617 .ok()
618 .map(FileContentsWrapper::new)
619 } else {
620 None
621 };
622 let symbol_map =
623 breakpad::get_symbol_map_for_breakpad_sym(file_contents, index_file_contents)?;
624 Ok(SymbolMap::new_plain(file_location, Box::new(symbol_map)))
625 } else if jitdump::is_jitdump_file(&file_contents) {
626 jitdump::get_symbol_map_for_jitdump(file_contents, file_location)
627 } else {
628 Err(Error::InvalidInputError(
629 "The file does not have a known format; PDB::open was not able to parse it and object::FileKind::parse was not able to detect the format.",
630 ))
631 }
632 }
633
634 pub async fn load_binary_at_location(
635 &self,
636 file_location: H::FL,
637 name: Option<String>,
638 path: Option<String>,
639 multi_arch_disambiguator: Option<MultiArchDisambiguator>,
640 ) -> Result<BinaryImage<F>, Error> {
641 let file_contents = self
642 .helper
643 .load_file(file_location.clone())
644 .await
645 .map_err(|e| Error::HelperErrorDuringOpenFile(file_location.to_string(), e))?;
646
647 let file_contents = FileContentsWrapper::new(file_contents);
648
649 let file_kind = match FileKind::parse(&file_contents) {
650 Ok(file_kind) => file_kind,
651 Err(_) if jitdump::is_jitdump_file(&file_contents) => {
652 let cursor = FileContentsCursor::new(&file_contents);
653 let reader = JitDumpReader::new(cursor)?;
654 let index = JitDumpIndex::from_reader(reader).map_err(Error::JitDumpFileReading)?;
655 let inner = BinaryImageInner::JitDump(file_contents, index);
656 return BinaryImage::new(inner, name, path);
657 }
658 Err(_) => {
659 return Err(Error::InvalidInputError("Unrecognized file"));
660 }
661 };
662 let inner = match file_kind {
663 FileKind::Elf32
664 | FileKind::Elf64
665 | FileKind::MachO32
666 | FileKind::MachO64
667 | FileKind::Pe32
668 | FileKind::Pe64 => BinaryImageInner::Normal(file_contents, file_kind),
669 FileKind::MachOFat32 | FileKind::MachOFat64 => {
670 let member = macho::get_fat_archive_member(
671 &file_contents,
672 file_kind,
673 multi_arch_disambiguator,
674 )?;
675 let (offset, size) = member.offset_and_size;
676 let arch = member.arch;
677 let data = macho::MachOFatArchiveMemberData::new(file_contents, offset, size, arch);
678 BinaryImageInner::MemberOfFatArchive(data, file_kind)
679 }
680 _ => {
681 return Err(Error::InvalidInputError(
682 "Input was Archive, Coff or Wasm format, which are unsupported for now",
683 ))
684 }
685 };
686 BinaryImage::new(inner, name, path)
687 }
688}