samply_symbols/
macho.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use debugid::DebugId;
5use macho_unwind_info::UnwindInfo;
6use object::macho::{self, LinkeditDataCommand, MachHeader32, MachHeader64};
7use object::read::macho::{
8    FatArch, LoadCommandIterator, MachHeader, MachOFatFile32, MachOFatFile64,
9};
10use object::read::{File, Object, ObjectSection};
11use object::{Endianness, FileKind, ReadRef};
12use uuid::Uuid;
13use yoke::Yoke;
14use yoke_derive::Yokeable;
15
16use crate::binary_image::{BinaryImage, BinaryImageInner};
17use crate::debugid_util::debug_id_for_object;
18use crate::dwarf::Addr2lineContextData;
19use crate::error::Error;
20use crate::shared::{
21    FileAndPathHelper, FileContents, FileContentsWrapper, FileLocation, MultiArchDisambiguator,
22    RangeReadRef,
23};
24use crate::symbol_map::SymbolMap;
25use crate::symbol_map_object::{
26    ObjectSymbolMap, ObjectSymbolMapInnerWrapper, ObjectSymbolMapOuter,
27};
28
29/// Converts a cpu type/subtype pair into the architecture name.
30///
31/// For example, this converts `CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E` to `Some("arm64e")`.
32fn macho_arch_name_for_cpu_type(cputype: u32, cpusubtype: u32) -> Option<&'static str> {
33    use object::macho::*;
34    let s = match (cputype, cpusubtype) {
35        (CPU_TYPE_X86, _) => "i386",
36        (CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H) => "x86_64h",
37        (CPU_TYPE_X86_64, _) => "x86_64",
38        (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E) => "arm64e",
39        (CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8) => "arm64v8",
40        (CPU_TYPE_ARM64, _) => "arm64",
41        (CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8) => "arm64_32v8",
42        (CPU_TYPE_ARM64_32, _) => "arm64_32",
43        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ) => "armv5",
44        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6) => "armv6",
45        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M) => "armv6m",
46        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7) => "armv7",
47        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F) => "armv7f",
48        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S) => "armv7s",
49        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K) => "armv7k",
50        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M) => "armv7m",
51        (CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM) => "armv7em",
52        (CPU_TYPE_ARM, _) => "arm",
53        (CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL) => "ppc",
54        (CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL) => "ppc64",
55        _ => return None,
56    };
57    Some(s)
58}
59
60/// Returns the (offset, size, arch) in the fat binary file for the object that matches
61/// `disambiguator`, if found.
62///
63/// If `disambiguator` is `None`, this will always return [`Error::NoDisambiguatorForFatArchive`].
64pub fn get_fat_archive_member(
65    file_contents: &FileContentsWrapper<impl FileContents>,
66    archive_kind: FileKind,
67    disambiguator: Option<MultiArchDisambiguator>,
68) -> Result<FatArchiveMember, Error> {
69    let mut members = get_fat_archive_members(file_contents, archive_kind)?;
70
71    if members.is_empty() {
72        return Err(Error::EmptyFatArchive);
73    }
74
75    if members.len() == 1 && disambiguator.is_none() {
76        return Ok(members.remove(0));
77    }
78
79    let disambiguator = match disambiguator {
80        Some(disambiguator) => disambiguator,
81        None => return Err(Error::NoDisambiguatorForFatArchive(members)),
82    };
83
84    match members
85        .iter()
86        .enumerate()
87        .filter_map(|(i, m)| {
88            m.match_score_for_disambiguator(&disambiguator)
89                .map(|score| (i, score))
90        })
91        .min_by_key(|(_i, score)| *score)
92    {
93        Some((i, _score)) => Ok(members.remove(i)),
94        None => Err(Error::NoMatchMultiArch(members)),
95    }
96}
97
98pub fn get_fat_archive_members_impl<FC: FileContents, FA: FatArch>(
99    file_contents: &FileContentsWrapper<FC>,
100    arches: &[FA],
101) -> Result<Vec<FatArchiveMember>, Error> {
102    let mut members = Vec::new();
103
104    for fat_arch in arches {
105        let (cputype, cpusubtype) = (fat_arch.cputype(), fat_arch.cpusubtype());
106        let arch = macho_arch_name_for_cpu_type(cputype, cpusubtype).map(ToString::to_string);
107        let (start, size) = fat_arch.file_range();
108        let file =
109            File::parse(file_contents.range(start, size)).map_err(Error::MachOHeaderParseError)?;
110        let uuid = file.mach_uuid().ok().flatten().map(Uuid::from_bytes);
111        members.push(FatArchiveMember {
112            offset_and_size: (start, size),
113            cputype,
114            cpusubtype,
115            arch,
116            uuid,
117        });
118    }
119
120    Ok(members)
121}
122
123#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
124pub struct FatArchiveMember {
125    pub offset_and_size: (u64, u64),
126    pub cputype: u32,
127    pub cpusubtype: u32,
128    pub arch: Option<String>,
129    pub uuid: Option<Uuid>,
130}
131
132impl FatArchiveMember {
133    /// Returns `None` if it doesn't match.
134    /// Returns `Some(_)` if there is a match, and lower values are better.
135    pub fn match_score_for_disambiguator(
136        &self,
137        disambiguator: &MultiArchDisambiguator,
138    ) -> Option<usize> {
139        match disambiguator {
140            MultiArchDisambiguator::Arch(expected_arch) => {
141                if self.arch.as_deref() == Some(expected_arch) {
142                    Some(0)
143                } else {
144                    None
145                }
146            }
147            MultiArchDisambiguator::BestMatch(expected_archs) => {
148                if let Some(arch) = self.arch.as_deref() {
149                    expected_archs.iter().position(|ea| ea == arch)
150                } else {
151                    None
152                }
153            }
154            MultiArchDisambiguator::BestMatchForNative => {
155                if let Some(arch) = self.arch.as_deref() {
156                    #[cfg(target_arch = "x86_64")]
157                    match arch {
158                        "x86_64h" => Some(0),
159                        "x86_64" => Some(1),
160                        _ => None,
161                    }
162                    #[cfg(target_arch = "aarch64")]
163                    match arch {
164                        "arm64e" => Some(0),
165                        "arm64" => Some(1),
166                        _ => None,
167                    }
168                    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
169                    {
170                        let _ = arch;
171                        None
172                    }
173                } else {
174                    None
175                }
176            }
177            MultiArchDisambiguator::DebugId(expected_debug_id) => {
178                if self.uuid.map(DebugId::from_uuid) == Some(*expected_debug_id) {
179                    Some(0)
180                } else {
181                    None
182                }
183            }
184        }
185    }
186}
187
188pub fn get_fat_archive_members(
189    file_contents: &FileContentsWrapper<impl FileContents>,
190    archive_kind: FileKind,
191) -> Result<Vec<FatArchiveMember>, Error> {
192    if archive_kind == FileKind::MachOFat64 {
193        let fat_file = MachOFatFile64::parse(file_contents)
194            .map_err(|e| Error::ObjectParseError(archive_kind, e))?;
195        get_fat_archive_members_impl(file_contents, fat_file.arches())
196    } else {
197        let fat_file = MachOFatFile32::parse(file_contents)
198            .map_err(|e| Error::ObjectParseError(archive_kind, e))?;
199        get_fat_archive_members_impl(file_contents, fat_file.arches())
200    }
201}
202
203struct DyldCacheLoader<'a, H>
204where
205    H: FileAndPathHelper,
206{
207    helper: &'a H,
208    dyld_cache_path: &'a H::FL,
209}
210
211impl<'a, H, F> DyldCacheLoader<'a, H>
212where
213    H: FileAndPathHelper<F = F>,
214{
215    pub fn new(helper: &'a H, dyld_cache_path: &'a H::FL) -> Self {
216        Self {
217            helper,
218            dyld_cache_path,
219        }
220    }
221
222    pub async fn load_cache(&self) -> Result<F, Error> {
223        self.helper
224            .load_file(self.dyld_cache_path.clone())
225            .await
226            .map_err(|e| Error::HelperErrorDuringOpenFile(self.dyld_cache_path.to_string(), e))
227    }
228
229    pub async fn load_subcache(&self, suffix: &str) -> Result<F, Error> {
230        let subcache_location = self
231            .dyld_cache_path
232            .location_for_dyld_subcache(suffix)
233            .ok_or(Error::FileLocationRefusedSubcacheLocation)?;
234        self.helper
235            .load_file(subcache_location)
236            .await
237            .map_err(|e| Error::HelperErrorDuringOpenFile(self.dyld_cache_path.to_string(), e))
238    }
239}
240
241async fn load_file_data_for_dyld_cache<H, F>(
242    dyld_cache_path: H::FL,
243    dylib_path: String,
244    helper: &H,
245) -> Result<DyldCacheFileData<F>, Error>
246where
247    H: FileAndPathHelper<F = F>,
248    F: FileContents + 'static,
249{
250    let dcl = DyldCacheLoader::new(helper, &dyld_cache_path);
251    let root_contents = dcl.load_cache().await?;
252    let root_contents = FileContentsWrapper::new(root_contents);
253
254    let mut subcache_contents = Vec::new();
255    for subcache_index in 1.. {
256        // Find the subcache at dyld_shared_cache_arm64e.1 or dyld_shared_cache_arm64e.01
257        let suffix = format!(".{subcache_index}");
258        let suffix2 = format!(".{subcache_index:02}");
259        let subcache = match dcl.load_subcache(&suffix).await {
260            Ok(subcache) => subcache,
261            Err(_) => match dcl.load_subcache(&suffix2).await {
262                Ok(subcache) => subcache,
263                Err(_) => break,
264            },
265        };
266        subcache_contents.push(FileContentsWrapper::new(subcache));
267    }
268    if let Ok(subcache) = dcl.load_subcache(".symbols").await {
269        subcache_contents.push(FileContentsWrapper::new(subcache));
270    };
271
272    Ok(DyldCacheFileData::new(
273        root_contents,
274        subcache_contents,
275        dylib_path,
276    ))
277}
278
279pub async fn load_symbol_map_for_dyld_cache<H>(
280    dyld_cache_path: H::FL,
281    dylib_path: String,
282    helper: &H,
283) -> Result<SymbolMap<H>, Error>
284where
285    H: FileAndPathHelper,
286{
287    let owner = load_file_data_for_dyld_cache(dyld_cache_path.clone(), dylib_path, helper).await?;
288    let owner = FileDataAndObject::new(Box::new(owner))?;
289    let symbol_map = ObjectSymbolMap::new(owner)?;
290    Ok(SymbolMap::new_plain(dyld_cache_path, Box::new(symbol_map)))
291}
292
293pub struct DyldCacheFileData<T>
294where
295    T: FileContents + 'static,
296{
297    root_file_data: FileContentsWrapper<T>,
298    subcache_file_data: Vec<FileContentsWrapper<T>>,
299    dylib_path: String,
300}
301
302type FileContentsRange<'data, T> = RangeReadRef<'data, &'data FileContentsWrapper<T>>;
303
304#[derive(Yokeable)]
305pub struct ObjectAndMachOData<'data, T: FileContents + 'static> {
306    object: File<'data, FileContentsRange<'data, T>>,
307    macho_data: MachOData<'data, FileContentsRange<'data, T>>,
308    addr2line_context: Addr2lineContextData,
309}
310
311impl<'data, T: FileContents + 'static> ObjectAndMachOData<'data, T> {
312    pub fn new(
313        object: File<'data, FileContentsRange<'data, T>>,
314        macho_data: MachOData<'data, FileContentsRange<'data, T>>,
315    ) -> Self {
316        Self {
317            object,
318            macho_data,
319            addr2line_context: Addr2lineContextData::new(),
320        }
321    }
322
323    pub fn into_parts(
324        self,
325    ) -> (
326        File<'data, FileContentsRange<'data, T>>,
327        MachOData<'data, FileContentsRange<'data, T>>,
328    ) {
329        (self.object, self.macho_data)
330    }
331}
332
333trait MakeMachObject<T: FileContents + 'static> {
334    fn file_data(&self) -> RangeReadRef<'_, &'_ FileContentsWrapper<T>>;
335    fn make_dependent_object(&self) -> Result<ObjectAndMachOData<'_, T>, Error>;
336}
337
338impl<T: FileContents + 'static> DyldCacheFileData<T> {
339    pub fn new(
340        root_file_data: FileContentsWrapper<T>,
341        subcache_file_data: Vec<FileContentsWrapper<T>>,
342        dylib_path: String,
343    ) -> Self {
344        Self {
345            root_file_data,
346            subcache_file_data,
347            dylib_path,
348        }
349    }
350
351    pub fn make_object(&self) -> Result<ObjectAndMachOData<'_, T>, Error> {
352        let rootcache_range = self.root_file_data.full_range();
353        let subcache_ranges: Vec<_> = self
354            .subcache_file_data
355            .iter()
356            .map(FileContentsWrapper::full_range)
357            .collect();
358        let cache = object::read::macho::DyldCache::<Endianness, _>::parse(
359            rootcache_range,
360            &subcache_ranges,
361        )
362        .map_err(Error::DyldCacheParseError)?;
363
364        let image = match cache
365            .images()
366            .find(|image| image.path() == Ok(&self.dylib_path))
367        {
368            Some(image) => image,
369            None => return Err(Error::NoMatchingDyldCacheImagePath(self.dylib_path.clone())),
370        };
371        let object = image.parse_object().map_err(Error::MachOHeaderParseError)?;
372        let (data, header_offset) = image
373            .image_data_and_offset()
374            .map_err(Error::MachOHeaderParseError)?;
375        let macho_data = MachOData::new(data, header_offset, object.is_64());
376        Ok(ObjectAndMachOData::new(object, macho_data))
377    }
378}
379
380impl<T: FileContents + 'static> MakeMachObject<T> for DyldCacheFileData<T> {
381    fn file_data(&self) -> RangeReadRef<'_, &'_ FileContentsWrapper<T>> {
382        self.root_file_data.full_range()
383    }
384    fn make_dependent_object(&self) -> Result<ObjectAndMachOData<'_, T>, Error> {
385        self.make_object()
386    }
387}
388
389struct FileDataAndObject<T: FileContents + 'static>(
390    Yoke<ObjectAndMachOData<'static, T>, Box<dyn MakeMachObject<T> + Send + Sync>>,
391);
392
393impl<T: FileContents + 'static> FileDataAndObject<T> {
394    pub fn new(data: Box<dyn MakeMachObject<T> + Send + Sync>) -> Result<Self, Error> {
395        let owner_and_object = Yoke::try_attach_to_cart(data, |data| data.make_dependent_object())?;
396        Ok(Self(owner_and_object))
397    }
398}
399
400impl<T: FileContents + 'static> ObjectSymbolMapOuter<T> for FileDataAndObject<T> {
401    fn make_symbol_map_inner(&self) -> Result<ObjectSymbolMapInnerWrapper<'_, T>, Error> {
402        let ObjectAndMachOData {
403            object,
404            macho_data,
405            addr2line_context,
406        } = self.0.get();
407        let (function_starts, function_ends) = compute_function_addresses_macho(macho_data, object);
408        let debug_id = debug_id_for_object(object)
409            .ok_or(Error::InvalidInputError("debug ID cannot be read"))?;
410        let symbol_map = ObjectSymbolMapInnerWrapper::new(
411            object,
412            addr2line_context
413                .make_context(macho_data.data, object, None, None)
414                .ok(),
415            None,
416            debug_id,
417            function_starts.as_deref(),
418            function_ends.as_deref(),
419            &(),
420        );
421
422        Ok(symbol_map)
423    }
424}
425
426pub fn get_symbol_map_for_macho<H: FileAndPathHelper>(
427    debug_file_location: H::FL,
428    file_contents: FileContentsWrapper<H::F>,
429    helper: Arc<H>,
430) -> Result<SymbolMap<H>, Error> {
431    let owner = FileDataAndObject::new(Box::new(MachSymbolMapData(file_contents)))?;
432    let symbol_map = ObjectSymbolMap::new(owner)?;
433    Ok(SymbolMap::new_with_external_file_support(
434        debug_file_location,
435        Box::new(symbol_map),
436        helper,
437    ))
438}
439
440pub fn get_symbol_map_for_fat_archive_member<H: FileAndPathHelper>(
441    debug_file_location: H::FL,
442    file_contents: FileContentsWrapper<H::F>,
443    member: FatArchiveMember,
444    helper: Arc<H>,
445) -> Result<SymbolMap<H>, Error> {
446    let (start_offset, range_size) = member.offset_and_size;
447    let owner =
448        MachOFatArchiveMemberData::new(file_contents, start_offset, range_size, member.arch);
449    let owner = FileDataAndObject::new(Box::new(owner))?;
450    let symbol_map = ObjectSymbolMap::new(owner)?;
451    Ok(SymbolMap::new_with_external_file_support(
452        debug_file_location,
453        Box::new(symbol_map),
454        helper,
455    ))
456}
457
458struct MachSymbolMapData<T: FileContents>(FileContentsWrapper<T>);
459
460impl<T: FileContents + 'static> MakeMachObject<T> for MachSymbolMapData<T> {
461    fn file_data(&self) -> RangeReadRef<'_, &'_ FileContentsWrapper<T>> {
462        self.0.full_range()
463    }
464
465    fn make_dependent_object(&self) -> Result<ObjectAndMachOData<'_, T>, Error> {
466        let file_data = self.file_data();
467        let object = File::parse(file_data).map_err(Error::MachOHeaderParseError)?;
468        let macho_data = MachOData::new(file_data, 0, object.is_64());
469        Ok(ObjectAndMachOData::new(object, macho_data))
470    }
471}
472
473pub struct MachOFatArchiveMemberData<T: FileContents> {
474    file_data: FileContentsWrapper<T>,
475    start_offset: u64,
476    range_size: u64,
477    arch: Option<String>,
478}
479
480impl<T: FileContents> MachOFatArchiveMemberData<T> {
481    pub fn new(
482        file_data: FileContentsWrapper<T>,
483        start_offset: u64,
484        range_size: u64,
485        arch: Option<String>,
486    ) -> Self {
487        Self {
488            file_data,
489            start_offset,
490            range_size,
491            arch,
492        }
493    }
494
495    pub fn data(&self) -> RangeReadRef<&'_ FileContentsWrapper<T>> {
496        let file_contents_ref = &self.file_data;
497        file_contents_ref.range(self.start_offset, self.range_size)
498    }
499
500    pub fn arch(&self) -> Option<String> {
501        self.arch.clone()
502    }
503}
504
505impl<T: FileContents + 'static> MakeMachObject<T> for MachOFatArchiveMemberData<T> {
506    fn file_data(&self) -> RangeReadRef<'_, &'_ FileContentsWrapper<T>> {
507        self.data()
508    }
509
510    fn make_dependent_object(&self) -> Result<ObjectAndMachOData<'_, T>, Error> {
511        let object = File::parse(self.file_data()).map_err(Error::MachOHeaderParseError)?;
512        let macho_data = MachOData::new(self.file_data(), 0, object.is_64());
513        Ok(ObjectAndMachOData::new(object, macho_data))
514    }
515}
516
517pub async fn load_binary_from_dyld_cache<F, H>(
518    dyld_cache_path: H::FL,
519    dylib_path: String,
520    helper: &H,
521) -> Result<BinaryImage<F>, Error>
522where
523    F: FileContents + 'static,
524    H: FileAndPathHelper<F = F>,
525{
526    let file_data =
527        load_file_data_for_dyld_cache(dyld_cache_path, dylib_path.clone(), helper).await?;
528    let inner = BinaryImageInner::MemberOfDyldSharedCache(file_data);
529    let name = match dylib_path.rfind('/') {
530        Some(index) => dylib_path[index + 1..].to_owned(),
531        None => dylib_path.to_owned(),
532    };
533    let image = BinaryImage::new(inner, Some(name), Some(dylib_path))?;
534    Ok(image)
535}
536
537fn compute_function_addresses_macho<'data, O, R>(
538    macho_data: &MachOData<'data, R>,
539    object_file: &O,
540) -> (Option<Vec<u32>>, Option<Vec<u32>>)
541where
542    O: object::Object<'data>,
543    R: ReadRef<'data>,
544{
545    // Get function start addresses from LC_FUNCTION_STARTS
546    let mut function_starts = macho_data.get_function_starts().ok().flatten();
547
548    // and from __unwind_info.
549    if let Some(unwind_info) = object_file
550        .section_by_name_bytes(b"__unwind_info")
551        .and_then(|s| s.data().ok())
552        .and_then(|d| UnwindInfo::parse(d).ok())
553    {
554        let function_starts = function_starts.get_or_insert_with(Vec::new);
555        let mut iter = unwind_info.functions();
556        while let Ok(Some(function)) = iter.next() {
557            function_starts.push(function.start_address);
558        }
559    }
560
561    (function_starts, None)
562}
563
564#[derive(Clone, Copy)]
565pub struct MachOData<'data, R: ReadRef<'data>> {
566    data: R,
567    header_offset: u64,
568    is_64: bool,
569    _phantom: PhantomData<&'data ()>,
570}
571
572impl<'data, R: ReadRef<'data>> MachOData<'data, R> {
573    pub fn new(data: R, header_offset: u64, is_64: bool) -> Self {
574        Self {
575            data,
576            header_offset,
577            is_64,
578            _phantom: PhantomData,
579        }
580    }
581
582    /// Read the list of function start addresses from the LC_FUNCTION_STARTS mach-O load command.
583    /// This information is usually present even in stripped binaries. It's a uleb128 encoded list
584    /// of deltas between the function addresses, with a zero delta terminator.
585    /// We use this information to improve symbolication for stripped binaries: It allows us to
586    /// group addresses from the same function into the same (synthesized) "symbol". It also allows
587    /// better results for binaries with partial symbol tables, because it tells us where the
588    /// functions with symbols end. This means that those symbols don't "overreach" to cover
589    /// addresses after their function - instead, they get correctly terminated by a symbol-less
590    /// function's start address.
591    pub fn get_function_starts(&self) -> Result<Option<Vec<u32>>, Error> {
592        let data = self
593            .function_start_data()
594            .map_err(Error::MachOHeaderParseError)?;
595        let data = if let Some(data) = data {
596            data
597        } else {
598            return Ok(None);
599        };
600        let mut function_starts = Vec::new();
601        let mut prev_address = 0;
602        let mut bytes = data;
603        while let Some((delta, rest)) = read_uleb128(bytes) {
604            if delta == 0 {
605                break;
606            }
607            bytes = rest;
608            let address = prev_address + delta;
609            function_starts.push(address as u32);
610            prev_address = address;
611        }
612
613        Ok(Some(function_starts))
614    }
615
616    pub fn get_arch(&self) -> Option<&'static str> {
617        if self.is_64 {
618            self.get_arch_impl::<MachHeader64<Endianness>>()
619        } else {
620            self.get_arch_impl::<MachHeader32<Endianness>>()
621        }
622    }
623
624    fn get_arch_impl<M: MachHeader>(&self) -> Option<&'static str> {
625        let header = M::parse(self.data, self.header_offset).ok()?;
626        let endian = header.endian().ok()?;
627        macho_arch_name_for_cpu_type(header.cputype(endian), header.cpusubtype(endian))
628    }
629
630    fn load_command_iter<M: MachHeader>(
631        &self,
632    ) -> object::read::Result<(M::Endian, LoadCommandIterator<M::Endian>)> {
633        let header = M::parse(self.data, self.header_offset)?;
634        let endian = header.endian()?;
635        let load_commands = header.load_commands(endian, self.data, self.header_offset)?;
636        Ok((endian, load_commands))
637    }
638
639    fn function_start_data(&self) -> object::read::Result<Option<&'data [u8]>> {
640        let (endian, mut commands) = if self.is_64 {
641            self.load_command_iter::<MachHeader64<Endianness>>()?
642        } else {
643            self.load_command_iter::<MachHeader32<Endianness>>()?
644        };
645        while let Ok(Some(command)) = commands.next() {
646            if command.cmd() == macho::LC_FUNCTION_STARTS {
647                let command: &LinkeditDataCommand<_> = command.data()?;
648                let dataoff: u64 = command.dataoff.get(endian).into();
649                let datasize: u64 = command.datasize.get(endian).into();
650                let data = self.data.read_bytes_at(dataoff, datasize).ok();
651                return Ok(data);
652            }
653        }
654        Ok(None)
655    }
656}
657
658fn read_uleb128(mut bytes: &[u8]) -> Option<(u64, &[u8])> {
659    const CONTINUATION_BIT: u8 = 1 << 7;
660
661    let mut result = 0;
662    let mut shift = 0;
663
664    while !bytes.is_empty() {
665        let byte = bytes[0];
666        bytes = &bytes[1..];
667        if shift == 63 && byte != 0x00 && byte != 0x01 {
668            return None;
669        }
670
671        let low_bits = u64::from(byte & !CONTINUATION_BIT);
672        result |= low_bits << shift;
673
674        if byte & CONTINUATION_BIT == 0 {
675            return Some((result, bytes));
676        }
677
678        shift += 7;
679    }
680    None
681}