diff --git a/src/bloaty.cc b/src/bloaty.cc index f489c60..8b025b8 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1148,7 +1148,7 @@ bool RangeSink::IsVerboseForVMRange(uint64_t vmaddr, uint64_t vmsize) { RangeMap vm_map; RangeMap file_map; bool contains = false; - vm_map.AddRangeWithTranslation(vmaddr, vmsize, "", translator_->vm_map, + vm_map.AddRangeWithTranslation(vmaddr, vmsize, "", translator_->GetVMMap(segment_id_), false, &file_map); file_map.ForEachRange( [this, &contains](uint64_t fileoff, uint64_t filesize) { @@ -1181,7 +1181,7 @@ bool RangeSink::IsVerboseForFileRange(uint64_t fileoff, uint64_t filesize) { RangeMap file_map; bool contains = false; file_map.AddRangeWithTranslation(fileoff, filesize, "", - translator_->file_map, false, &vm_map); + translator_->GetFileMap(), false, &vm_map); vm_map.ForEachRange([this, &contains](uint64_t vmaddr, uint64_t vmsize) { if (ContainsVerboseVMAddr(vmaddr, vmsize)) { contains = true; @@ -1208,15 +1208,15 @@ void RangeSink::AddFileRange(const char* analyzer, string_view name, for (auto& pair : outputs_) { const std::string label = pair.second->Munge(name); if (translator_) { - bool ok = pair.first->file_map.AddRangeWithTranslation( - fileoff, filesize, label, translator_->file_map, verbose, - &pair.first->vm_map); + bool ok = pair.first->GetFileMap().AddRangeWithTranslation( + fileoff, filesize, label, translator_->GetFileMap(), verbose, + &pair.first->GetVMMap(segment_id_)); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", fileoff, filesize, name); } } else { - pair.first->file_map.AddRange(fileoff, filesize, label); + pair.first->GetFileMap().AddRange(fileoff, filesize, label); } } } @@ -1234,10 +1234,10 @@ void RangeSink::AddFileRangeForVMAddr(const char* analyzer, assert(translator_); for (auto& pair : outputs_) { std::string label; - if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) { - bool ok = pair.first->file_map.AddRangeWithTranslation( - file_offset, file_range.size(), label, translator_->file_map, verbose, - &pair.first->vm_map); + if (pair.first->GetVMMap(segment_id_).TryGetLabel(label_from_vmaddr, &label)) { + bool ok = pair.first->GetFileMap().AddRangeWithTranslation( + file_offset, file_range.size(), label, translator_->GetFileMap(), verbose, + &pair.first->GetVMMap(segment_id_)); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", file_offset, file_range.size(), label); @@ -1263,11 +1263,11 @@ void RangeSink::AddFileRangeForFileRange(const char* analyzer, assert(translator_); for (auto& pair : outputs_) { std::string label; - if (pair.first->file_map.TryGetLabelForRange( + if (pair.first->GetFileMap().TryGetLabelForRange( from_file_offset, from_file_range.size(), &label)) { - bool ok = pair.first->file_map.AddRangeWithTranslation( - file_offset, file_range.size(), label, translator_->file_map, verbose, - &pair.first->vm_map); + bool ok = pair.first->GetFileMap().AddRangeWithTranslation( + file_offset, file_range.size(), label, translator_->GetFileMap(), verbose, + &pair.first->GetVMMap(segment_id_)); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", file_offset, file_range.size(), label); @@ -1292,10 +1292,10 @@ void RangeSink::AddVMRangeForVMAddr(const char* analyzer, assert(translator_); for (auto& pair : outputs_) { std::string label; - if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) { - bool ok = pair.first->vm_map.AddRangeWithTranslation( - addr, size, label, translator_->vm_map, verbose, - &pair.first->file_map); + if (pair.first->GetVMMap(segment_id_).TryGetLabel(label_from_vmaddr, &label)) { + bool ok = pair.first->GetVMMap(segment_id_).AddRangeWithTranslation( + addr, size, label, translator_->GetVMMap(segment_id_), verbose, + &pair.first->GetFileMap()); if (!ok && verbose_level > 1) { WARN("VM range ($0, $1) for label $2 extends beyond base map", addr, size, label); @@ -1317,9 +1317,9 @@ void RangeSink::AddVMRange(const char* analyzer, uint64_t vmaddr, assert(translator_); for (auto& pair : outputs_) { const std::string label = pair.second->Munge(name); - bool ok = pair.first->vm_map.AddRangeWithTranslation( - vmaddr, vmsize, label, translator_->vm_map, verbose, - &pair.first->file_map); + bool ok = pair.first->GetVMMap(segment_id_).AddRangeWithTranslation( + vmaddr, vmsize, label, translator_->GetVMMap(segment_id_), verbose, + &pair.first->GetFileMap()); if (!ok) { WARN("VM range ($0, $1) for label $2 extends beyond base map", vmaddr, vmsize, name); @@ -1360,8 +1360,8 @@ void RangeSink::AddRange(const char* analyzer, string_view name, } if (translator_) { - if (!translator_->vm_map.CoversRange(vmaddr, vmsize) || - !translator_->file_map.CoversRange(fileoff, filesize)) { + if (!translator_->GetVMMap(segment_id_).CoversRange(vmaddr, vmsize) || + !translator_->GetFileMap().CoversRange(fileoff, filesize)) { WARN("AddRange($0, $1, $2, $3, $4) will be ignored, because it is not " "covered by base map.", name.data(), vmaddr, vmsize, fileoff, filesize); @@ -1373,11 +1373,11 @@ void RangeSink::AddRange(const char* analyzer, string_view name, const std::string label = pair.second->Munge(name); uint64_t common = std::min(vmsize, filesize); - pair.first->vm_map.AddDualRange(vmaddr, common, fileoff, label); - pair.first->file_map.AddDualRange(fileoff, common, vmaddr, label); + pair.first->GetVMMap(segment_id_).AddDualRange(vmaddr, common, fileoff, label); + pair.first->GetFileMap().AddDualRange(fileoff, common, vmaddr, label); - pair.first->vm_map.AddRange(vmaddr + common, vmsize - common, label); - pair.first->file_map.AddRange(fileoff + common, filesize - common, label); + pair.first->GetVMMap(segment_id_).AddRange(vmaddr + common, vmsize - common, label); + pair.first->GetFileMap().AddRange(fileoff + common, filesize - common, label); } } @@ -1386,10 +1386,10 @@ uint64_t RangeSink::TranslateFileToVM(const char* ptr) { uint64_t offset = ptr - file_->data().data(); uint64_t translated; if (!FileContainsPointer(ptr) || - !translator_->file_map.Translate(offset, &translated)) { + !translator_->GetFileMap().Translate(offset, &translated)) { THROWF("Can't translate file offset ($0) to VM, contains: $1, map:\n$2", offset, FileContainsPointer(ptr), - translator_->file_map.DebugString().c_str()); + translator_->GetFileMap().DebugString().c_str()); } return translated; } @@ -1397,7 +1397,7 @@ uint64_t RangeSink::TranslateFileToVM(const char* ptr) { std::string_view RangeSink::TranslateVMToFile(uint64_t address) { assert(translator_); uint64_t translated; - if (!translator_->vm_map.Translate(address, &translated) || + if (!translator_->GetVMMap(segment_id_).Translate(address, &translated) || translated > file_->data().size()) { THROWF("Can't translate VM pointer ($0) to file", address); @@ -1863,14 +1863,33 @@ struct DualMaps { } void ComputeRollup(Rollup* rollup) { + // Collect all segment IDs across all maps. + std::set segment_ids; for (auto& map : maps_) { - map->vm_map.Compress(); - map->file_map.Compress(); + for (int id : map->VMSegmentIds()) { + segment_ids.insert(id); + } + } + // Compress and roll up each segment's VM maps separately. + for (int seg_id : segment_ids) { + std::vector vm_maps; + for (auto& map : maps_) { + if (map->HasVMMap(seg_id)) { + map->GetVMMap(seg_id).Compress(); + vm_maps.push_back(&map->GetVMMap(seg_id)); + } + } + if (!vm_maps.empty()) { + RangeMap::ComputeRollup(vm_maps, + [=](const std::vector& keys, uint64_t addr, uint64_t end) { + return rollup->AddSizes(keys, end - addr, true); + }); + } + } + // File maps are shared across all segments. + for (auto& map : maps_) { + map->GetFileMap().Compress(); } - RangeMap::ComputeRollup(VmMaps(), [=](const std::vector& keys, - uint64_t addr, uint64_t end) { - return rollup->AddSizes(keys, end - addr, true); - }); RangeMap::ComputeRollup( FileMaps(), [=](const std::vector& keys, uint64_t addr, uint64_t end) { @@ -1894,7 +1913,38 @@ struct DualMaps { } void PrintFileMaps() { PrintMaps(FileMaps()); } - void PrintVMMaps() { PrintMaps(VmMaps()); } + + void PrintVMMaps() { + std::set segment_ids; + for (auto& map : maps_) { + for (int id : map->VMSegmentIds()) { + segment_ids.insert(id); + } + } + for (int seg_id : segment_ids) { + if (segment_ids.size() > 1) { + auto it = segment_names_.find(seg_id); + if (it != segment_names_.end()) { + printf("%s:\n", it->second.c_str()); + } else { + printf("Segment %d:\n", seg_id); + } + } + std::vector vm_maps; + for (auto& map : maps_) { + if (map->HasVMMap(seg_id)) { + vm_maps.push_back(&map->GetVMMap(seg_id)); + } + } + if (!vm_maps.empty()) { + PrintMaps(vm_maps); + } + } + } + + void SetSegmentNames(std::map names) { + segment_names_ = std::move(names); + } std::string KeysToString(const std::vector& keys) { std::string ret; @@ -1920,23 +1970,16 @@ struct DualMaps { DualMap* base_map() { return maps_[0].get(); } private: - std::vector VmMaps() const { - std::vector ret; - for (const auto& map : maps_) { - ret.push_back(&map->vm_map); - } - return ret; - } - std::vector FileMaps() const { std::vector ret; for (const auto& map : maps_) { - ret.push_back(&map->file_map); + ret.push_back(&map->GetFileMap()); } return ret; } std::vector> maps_; + std::map segment_names_; }; void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, @@ -2001,14 +2044,19 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, rollup->file_total() + rollup->filtered_file_total(); file->ProcessFile(sink_ptrs); + maps.SetSegmentNames(file->GetSegmentNames()); + // kInputFile source: Copy the base map to the filename sink(s). for (auto sink : filename_sink_ptrs) { - maps.base_map()->vm_map.ForEachRange( - [sink](uint64_t start, uint64_t length) { - sink->AddVMRange("inputfile_vmcopier", start, length, - sink->input_file().filename()); - }); - maps.base_map()->file_map.ForEachRange( + for (int seg_id : maps.base_map()->VMSegmentIds()) { + sink->set_segment_id(seg_id); + maps.base_map()->GetVMMap(seg_id).ForEachRange( + [sink](uint64_t start, uint64_t length) { + sink->AddVMRange("inputfile_vmcopier", start, length, + sink->input_file().filename()); + }); + } + maps.base_map()->GetFileMap().ForEachRange( [sink](uint64_t start, uint64_t length) { sink->AddFileRange("inputfile_filecopier", sink->input_file().filename(), start, length); diff --git a/src/bloaty.h b/src/bloaty.h index 85515fc..69ea789 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -20,12 +20,14 @@ #define BLOATY_H_ #include +#include #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS #include #include #include +#include #include #include #include @@ -126,6 +128,8 @@ class RangeSink { DataSource data_source() const { return data_source_; } const InputFile &input_file() const { return *file_; } bool IsBaseMap() const { return translator_ == nullptr; } + int segment_id() const { return segment_id_; } + void set_segment_id(int segment_id) { segment_id_ = segment_id; } // If vmsize or filesize is zero, this mapping is presumed not to exist in // that domain. For example, .bss mappings don't exist in the file, and @@ -238,6 +242,7 @@ class RangeSink { const DualMap* translator_; std::vector> outputs_; google::protobuf::Arena *arena_; + int segment_id_ = 0; }; // NameMunger ////////////////////////////////////////////////////////////////// @@ -328,6 +333,11 @@ class ObjectFile { // given here, otherwise it is |this|. virtual void ProcessFile(const std::vector& sinks) const = 0; + // Optionally return human-readable names for segment e.g. "x86_64", or "arm64". + // Only meaningful for formats with multiple VM address spaces such + // as Mach-O universal binaries. + virtual std::map GetSegmentNames() const { return {}; } + virtual bool GetDisassemblyInfo(std::string_view symbol, DataSource symbol_source, DisassemblyInfo* info) const = 0; @@ -375,11 +385,47 @@ std::string ItaniumDemangle(std::string_view symbol, DataSource source); // DualMap ///////////////////////////////////////////////////////////////////// -// Contains a RangeMap for VM space and file space for a given file. +// Contains RangeMaps for VM space and file space for a given file. +// For universal binaries, each architecture slice has its own VM address space +// (keyed by segment_id). File offsets are shared across all segments and use a +// single RangeMap. For single-arch binaries, only segment_id 0 is used. struct DualMap { - RangeMap vm_map; + RangeMap& GetVMMap(int segment_id) { return vm_maps_[segment_id]; } + const RangeMap& GetVMMap(int segment_id) const { + auto it = vm_maps_.find(segment_id); + if (it == vm_maps_.end()) { + static const RangeMap empty; + return empty; + } + return it->second; + } + + RangeMap& GetFileMap() { return file_map; } + const RangeMap& GetFileMap() const { return file_map; } + + bool HasVMMap(int segment_id) const { + return vm_maps_.find(segment_id) != vm_maps_.end(); + } + + std::vector VMSegmentIds() const { + std::vector ids; + for (const auto& kv : vm_maps_) { + ids.push_back(kv.first); + } + return ids; + } + + RangeMap& vm_map() { + assert(HasVMMap(0)); + return GetVMMap(0); + } + const RangeMap& vm_map() const { return GetVMMap(0); } + RangeMap file_map; + + private: + std::map vm_maps_; }; struct DisassemblyInfo { diff --git a/src/disassemble.cc b/src/disassemble.cc index 4cf8839..2eb5f7a 100644 --- a/src/disassemble.cc +++ b/src/disassemble.cc @@ -212,7 +212,7 @@ std::string DisassembleFunction(const DisassemblyInfo& info) { } else { op_str = "<" + std::to_string(iter->second); } - } else if (info.symbol_map.vm_map.TryGetLabel(target, &label)) { + } else if (info.symbol_map.vm_map().TryGetLabel(target, &label)) { op_str = label; } } diff --git a/src/dwarf.cc b/src/dwarf.cc index eb68bc1..abb1f7f 100644 --- a/src/dwarf.cc +++ b/src/dwarf.cc @@ -440,7 +440,7 @@ void AddDIE(const dwarf::CU& cu, const GeneralDIE& die, // Unfortunately the location doesn't include a size, so we look that part // up in the symbol map. uint64_t size; - if (symbol_map.vm_map.TryGetSize(addr, &size)) { + if (symbol_map.GetVMMap(sink->segment_id()).TryGetSize(addr, &size)) { sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size, cu.unit_name()); } else { diff --git a/src/elf.cc b/src/elf.cc index ac0f610..d864cc6 100644 --- a/src/elf.cc +++ b/src/elf.cc @@ -950,7 +950,7 @@ static void ReadELFSymbols(const InputFile& file, RangeSink* sink, } // TODO(brandonvu) Continue if VM pointer cannot be translated. Issue #315 uint64_t unused; - if (!sink->Translator()->vm_map.Translate(full_addr, &unused)) { + if (!sink->Translator()->vm_map().Translate(full_addr, &unused)) { WARN("Can't translate VM pointer ($0) to file", full_addr); continue; } @@ -1462,7 +1462,7 @@ class ElfObjectFile : public ObjectFile { // symbolized. uint64_t fileoff; - if (!base_map.vm_map.Translate(vmaddr, &fileoff)) { + if (!base_map.vm_map().Translate(vmaddr, &fileoff)) { THROWF("Couldn't translate VM address for function $0", symbol); } diff --git a/src/macho.cc b/src/macho.cc index 3f849ac..aab6e72 100644 --- a/src/macho.cc +++ b/src/macho.cc @@ -24,6 +24,7 @@ #include "absl/strings/substitute.h" #include "third_party/darwin_xnu_macho/mach-o/loader.h" #include "third_party/darwin_xnu_macho/mach-o/fat.h" +#include "third_party/darwin_xnu_macho/mach/machine.h" #include "third_party/darwin_xnu_macho/mach-o/nlist.h" #include "third_party/darwin_xnu_macho/mach-o/reloc.h" @@ -74,6 +75,7 @@ struct LoadCommand { uint32_t cmd; string_view command_data; string_view file_data; + int segment_id = 0; // Architecture index for universal binaries, 0 otherwise. }; template @@ -84,7 +86,7 @@ bool Is64Bit() { return true; } template void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int segment_id, Func&& loadcmd_func) { string_view header_data = macho_data; auto header = GetStructPointerAndAdvance(&header_data); MaybeAddOverhead(overhead_sink, @@ -111,6 +113,7 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, data.cmd = command->cmd; data.command_data = StrictSubstr(header_data, 0, command->cmdsize); data.file_data = macho_data; + data.segment_id = segment_id; std::forward(loadcmd_func)(data); MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); @@ -120,7 +123,7 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, template void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int segment_id, Func&& loadcmd_func) { uint32_t magic = ReadMagic(macho_file); switch (magic) { case MH_MAGIC: @@ -131,12 +134,12 @@ void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, // Still, you can build 32-bit binaries as of this writing, and // there are existing 32-bit binaries floating around, so we might // as well support them. - ParseMachOHeaderImpl(macho_file, overhead_sink, + ParseMachOHeaderImpl(macho_file, overhead_sink, segment_id, std::forward(loadcmd_func)); break; case MH_MAGIC_64: ParseMachOHeaderImpl( - macho_file, overhead_sink, std::forward(loadcmd_func)); + macho_file, overhead_sink, segment_id, std::forward(loadcmd_func)); break; case MH_CIGAM: case MH_CIGAM_64: @@ -177,7 +180,7 @@ void ParseFatHeader(string_view fat_file, RangeSink* overhead_sink, auto arch = GetStructPointerAndAdvance(&header_data); string_view macho_data = StrictSubstr( fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); - ParseMachOHeader(macho_data, overhead_sink, + ParseMachOHeader(macho_data, overhead_sink, i, std::forward(loadcmd_func)); } } @@ -191,7 +194,7 @@ void ForEachLoadCommand(string_view maybe_fat_file, RangeSink* overhead_sink, case MH_MAGIC_64: case MH_CIGAM: case MH_CIGAM_64: - ParseMachOHeader(maybe_fat_file, overhead_sink, + ParseMachOHeader(maybe_fat_file, overhead_sink, 0, std::forward(loadcmd_func)); break; case FAT_CIGAM: @@ -439,7 +442,10 @@ void ParseLoadCommand(const LoadCommand& cmd, RangeSink* sink) { void ParseLoadCommands(RangeSink* sink) { ForEachLoadCommand( sink->input_file().data(), sink, - [sink](const LoadCommand& cmd) { ParseLoadCommand(cmd, sink); }); + [sink](const LoadCommand& cmd) { + sink->set_segment_id(cmd.segment_id); + ParseLoadCommand(cmd, sink); + }); } template @@ -490,6 +496,7 @@ void ParseSymbols(string_view file_data, SymbolTable* symtab, RangeSink* sink) { ForEachLoadCommand( file_data, sink, [symtab, sink](const LoadCommand& cmd) { + sink->set_segment_id(cmd.segment_id); switch (cmd.cmd) { case LC_SYMTAB: if (cmd.is64bit) { @@ -509,6 +516,7 @@ static void AddMachOFallback(RangeSink* sink) { ForEachLoadCommand( sink->input_file().data(), sink, [sink](const LoadCommand& cmd) { + sink->set_segment_id(cmd.segment_id); switch (cmd.cmd) { case LC_SEGMENT_64: AddSegmentAsFallback( @@ -580,6 +588,9 @@ static void ReadDebugSectionsFromMachO(const InputFile &file, dwarf->open = &ReadDebugSectionsFromMachO; ForEachLoadCommand( file.data(), nullptr, [dwarf, sink](const LoadCommand &cmd) { + if (sink) { + sink->set_segment_id(cmd.segment_id); + } switch (cmd.cmd) { case LC_SEGMENT_64: ReadDebugSectionsFromSegment( diff --git a/tests/macho/universal-binary.test b/tests/macho/universal-binary.test new file mode 100644 index 0000000..45aa424 --- /dev/null +++ b/tests/macho/universal-binary.test @@ -0,0 +1,225 @@ +# Test that bloaty correctly handles universal Mach-O binaries. +# +# Verifies that VM sizes from each architecture slice are reported +# independently and summed correctly. The two slices have different +# __text sizes (48 bytes for arm64, 64 bytes for x86_64) so any +# cross-contamination between address spaces would be detectable. +# +# RUN: %yaml2obj %s -o %t +# RUN: %bloaty --domain=vm -d sections %t | %FileCheck %s --check-prefix=VM-SECTIONS +# RUN: %bloaty --domain=vm -d segments %t | %FileCheck %s --check-prefix=VM-SEGMENTS +# RUN: %bloaty --domain=file -d sections %t | %FileCheck %s --check-prefix=FILE-SECTIONS + +# x86_64 slice: __TEXT,__text=64, __TEXT,__unwind_info=96, __TEXT vmsize=16384, __LINKEDIT vmsize=16384 +# arm64 slice: __TEXT,__text=48, __TEXT,__unwind_info=96, __TEXT vmsize=16384, __LINKEDIT vmsize=16384 +# Fat header + padding before slice 0 = [Unmapped] in file domain. +# Total VM: 2 * (16384 __TEXT + 16384 __LINKEDIT) = 65536 = 64Ki +# Total file: fat header (0x1000) + slice0 (0x8000) + slice1 (0x8000) = 68Ki + +# VM-SECTIONS: VM SIZE +# VM-SECTIONS-DAG: 192 __TEXT,__unwind_info +# VM-SECTIONS-DAG: 112 __TEXT,__text +# VM-SECTIONS: 64.0Ki TOTAL + +# VM-SEGMENTS: VM SIZE +# VM-SEGMENTS-DAG: 32.0Ki __LINKEDIT +# VM-SEGMENTS-DAG: 32.0Ki __TEXT +# VM-SEGMENTS: 64.0Ki TOTAL + +# FILE-SECTIONS: FILE SIZE +# FILE-SECTIONS: [Unmapped] +# FILE-SECTIONS-DAG: 192 __TEXT,__unwind_info +# FILE-SECTIONS-DAG: 112 __TEXT,__text +# FILE-SECTIONS: 68.0Ki TOTAL + +--- !fat-mach-o +FatHeader: + magic: 0xCAFEBABE + nfat_arch: 2 +FatArchs: + - cputype: 0x1000007 + cpusubtype: 0x3 + offset: 0x1000 + size: 0x8000 + align: 12 + - cputype: 0x100000C + cpusubtype: 0x0 + offset: 0x9000 + size: 0x8000 + align: 12 +Slices: +## x86_64 slice: __text size = 64 bytes (0x40) + - !mach-o + FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0x2 + ncmds: 7 + sizeofcmds: 456 + flags: 0x200085 + reserved: 0x0 + LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __PAGEZERO + vmaddr: 0 + vmsize: 4294967296 + fileoff: 0 + filesize: 0 + maxprot: 0 + initprot: 0 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 4294967296 + vmsize: 16384 + fileoff: 0 + filesize: 16384 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100000300 + size: 64 + offset: 0x300 + align: 4 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100000340 + size: 96 + offset: 0x340 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 010000001C000000000000001C000000000000001C00000002000000280300004000000040000000580300000000000040000000000000000000000000000000030000000C000200140002000000000008000001000000020000000400000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294983680 + vmsize: 16384 + fileoff: 16384 + filesize: 64 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_CHAINED_FIXUPS + cmdsize: 16 + dataoff: 16384 + datasize: 56 + - cmd: LC_DYLD_EXPORTS_TRIE + cmdsize: 16 + dataoff: 16440 + datasize: 8 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 16448 + datasize: 8 + - cmd: LC_CODE_SIGNATURE + cmdsize: 16 + dataoff: 16456 + datasize: 0 +## arm64 slice: __text size = 48 bytes (0x30) + - !mach-o + FileHeader: + magic: 0xFEEDFACF + cputype: 0x100000C + cpusubtype: 0x0 + filetype: 0x2 + ncmds: 7 + sizeofcmds: 456 + flags: 0x200085 + reserved: 0x0 + LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __PAGEZERO + vmaddr: 0 + vmsize: 4294967296 + fileoff: 0 + filesize: 0 + maxprot: 0 + initprot: 0 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 4294967296 + vmsize: 16384 + fileoff: 0 + filesize: 16384 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100000328 + size: 48 + offset: 0x328 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 40058052C0035FD6FF8300D1FD7B01A9FD430091BFC31FB8FAFFFF97E00B00B9E00B40B9FD7B41A9FF830091C0035FD6 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100000358 + size: 96 + offset: 0x358 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 010000001C000000000000001C000000000000001C00000002000000280300004000000040000000580300000000000040000000000000000000000000000000030000000C000200140002000000000008000001000000020000000400000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294983680 + vmsize: 16384 + fileoff: 16384 + filesize: 64 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_CHAINED_FIXUPS + cmdsize: 16 + dataoff: 16384 + datasize: 56 + - cmd: LC_DYLD_EXPORTS_TRIE + cmdsize: 16 + dataoff: 16440 + datasize: 8 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 16448 + datasize: 8 + - cmd: LC_CODE_SIGNATURE + cmdsize: 16 + dataoff: 16456 + datasize: 0