From 764da6411d20bcec6f4c0d220dcff08e0202f1c4 Mon Sep 17 00:00:00 2001 From: Ryan Mansfield Date: Mon, 2 Mar 2026 12:47:08 -0500 Subject: [PATCH] Add segment-aware VM addressing for universal binary support Introduces per-arch VM address spaces to allow multiple Mach-O architecture slices to coexist without address conflicts. RangeSink gains an arch_index field which gets passed to all ForEachLoadCommand calls. DualMap holds a map for VM space per arch_index while the file map remains shared. --- src/bloaty.cc | 152 +++++++++++++------- src/bloaty.h | 47 ++++++- src/disassemble.cc | 2 +- src/dwarf.cc | 2 +- src/elf.cc | 4 +- src/macho.cc | 45 +++++- tests/macho/universal-binary.test | 225 ++++++++++++++++++++++++++++++ 7 files changed, 412 insertions(+), 65 deletions(-) create mode 100644 tests/macho/universal-binary.test diff --git a/src/bloaty.cc b/src/bloaty.cc index 5232959c..84ab4f7c 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1149,7 +1149,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(arch_index_), false, &file_map); file_map.ForEachRange( [this, &contains](uint64_t fileoff, uint64_t filesize) { @@ -1182,7 +1182,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; @@ -1209,15 +1209,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(arch_index_)); 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); } } } @@ -1235,10 +1235,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(arch_index_).TryGetLabel(label_from_vmaddr, &label)) { + bool ok = pair.first->GetFileMap().AddRangeWithTranslation( + file_offset, file_range.size(), label, translator_->GetFileMap(), verbose, + &pair.first->GetVMMap(arch_index_)); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", file_offset, file_range.size(), label); @@ -1264,11 +1264,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(arch_index_)); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", file_offset, file_range.size(), label); @@ -1293,10 +1293,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(arch_index_).TryGetLabel(label_from_vmaddr, &label)) { + bool ok = pair.first->GetVMMap(arch_index_).AddRangeWithTranslation( + addr, size, label, translator_->GetVMMap(arch_index_), verbose, + &pair.first->GetFileMap()); if (!ok && verbose_level > 1) { WARN("VM range ($0, $1) for label $2 extends beyond base map", addr, size, label); @@ -1318,9 +1318,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(arch_index_).AddRangeWithTranslation( + vmaddr, vmsize, label, translator_->GetVMMap(arch_index_), verbose, + &pair.first->GetFileMap()); if (!ok) { WARN("VM range ($0, $1) for label $2 extends beyond base map", vmaddr, vmsize, name); @@ -1361,8 +1361,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(arch_index_).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); @@ -1374,11 +1374,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(arch_index_).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(arch_index_).AddRange(vmaddr + common, vmsize - common, label); + pair.first->GetFileMap().AddRange(fileoff + common, filesize - common, label); } } @@ -1387,10 +1387,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; } @@ -1398,7 +1398,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(arch_index_).Translate(address, &translated) || translated > file_->data().size()) { THROWF("Can't translate VM pointer ($0) to file", address); @@ -1864,14 +1864,33 @@ struct DualMaps { } void ComputeRollup(Rollup* rollup) { + // Collect all arch indices across all maps. + std::set arch_indices; for (auto& map : maps_) { - map->vm_map.Compress(); - map->file_map.Compress(); + for (int id : map->VMArchIndices()) { + arch_indices.insert(id); + } + } + // Compress and roll up each arch's VM maps separately. + for (int arch_id : arch_indices) { + std::vector vm_maps; + for (auto& map : maps_) { + if (map->HasVMMap(arch_id)) { + map->GetVMMap(arch_id).Compress(); + vm_maps.push_back(&map->GetVMMap(arch_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 arches. + 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) { @@ -1895,7 +1914,38 @@ struct DualMaps { } void PrintFileMaps() { PrintMaps(FileMaps()); } - void PrintVMMaps() { PrintMaps(VmMaps()); } + + void PrintVMMaps() { + std::set arch_indices; + for (auto& map : maps_) { + for (int id : map->VMArchIndices()) { + arch_indices.insert(id); + } + } + for (int arch_id : arch_indices) { + if (arch_indices.size() > 1) { + auto it = arch_names_.find(arch_id); + if (it != arch_names_.end()) { + printf("%s:\n", it->second.c_str()); + } else { + printf("arch %d:\n", arch_id); + } + } + std::vector vm_maps; + for (auto& map : maps_) { + if (map->HasVMMap(arch_id)) { + vm_maps.push_back(&map->GetVMMap(arch_id)); + } + } + if (!vm_maps.empty()) { + PrintMaps(vm_maps); + } + } + } + + void SetArchNames(std::map names) { + arch_names_ = std::move(names); + } std::string KeysToString(const std::vector& keys) { std::string ret; @@ -1921,23 +1971,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 arch_names_; }; void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, @@ -2002,14 +2045,19 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, rollup->file_total() + rollup->filtered_file_total(); file->ProcessFile(sink_ptrs); + maps.SetArchNames(file->GetArchNames()); + // 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 arch_id : maps.base_map()->VMArchIndices()) { + sink->set_arch_index(arch_id); + maps.base_map()->GetVMMap(arch_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 c9e2d1f4..e85fd152 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 @@ -127,6 +129,8 @@ class RangeSink { DataSource data_source() const { return data_source_; } const InputFile &input_file() const { return *file_; } bool IsBaseMap() const { return translator_ == nullptr; } + int arch_index() const { return arch_index_; } + void set_arch_index(int arch_index) { arch_index_ = arch_index; } // 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 @@ -239,6 +243,7 @@ class RangeSink { const DualMap* translator_; std::vector> outputs_; google::protobuf::Arena *arena_; + int arch_index_ = 0; }; // NameMunger ////////////////////////////////////////////////////////////////// @@ -329,6 +334,11 @@ class ObjectFile { // given here, otherwise it is |this|. virtual void ProcessFile(const std::vector& sinks) const = 0; + // Optionally return human-readable names keyed by arch index, e.g. + // {0: "x86_64", 1: "arm64"}. Only meaningful for formats with multiple VM + // address spaces, such as Mach-O universal binaries. + virtual std::map GetArchNames() const { return {}; } + virtual bool GetDisassemblyInfo(std::string_view symbol, DataSource symbol_source, DisassemblyInfo* info) const = 0; @@ -376,11 +386,44 @@ 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 arch_index). File offsets are shared across all arches and use a +// single RangeMap. For single-arch binaries, only arch_index 0 is used. struct DualMap { - RangeMap vm_map; + RangeMap& GetVMMap(int arch_index) { return vm_maps_[arch_index]; } + const RangeMap& GetVMMap(int arch_index) const { + auto it = vm_maps_.find(arch_index); + 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 arch_index) const { + return vm_maps_.find(arch_index) != vm_maps_.end(); + } + + std::vector VMArchIndices() const { + std::vector ids; + for (const auto& kv : vm_maps_) { + ids.push_back(kv.first); + } + return ids; + } + + RangeMap& vm_map() { 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 4cf88393..2eb5f7a8 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 eb68bc17..8704a353 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->arch_index()).TryGetSize(addr, &size)) { sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size, cu.unit_name()); } else { diff --git a/src/elf.cc b/src/elf.cc index de2df46d..aaa6dd65 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; } @@ -1464,7 +1464,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 5b8ca828..d6ac823a 100644 --- a/src/macho.cc +++ b/src/macho.cc @@ -127,6 +127,7 @@ struct LoadCommand { uint32_t cmd; string_view command_data; string_view file_data; + int arch_index = 0; // Architecture index for universal binaries, 0 otherwise. }; template @@ -137,7 +138,7 @@ bool Is64Bit() { return true; } template void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int arch_index, Func&& loadcmd_func) { string_view header_data = macho_data; auto header = GetStructPointerAndAdvance(&header_data); MaybeAddOverhead(overhead_sink, @@ -164,6 +165,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.arch_index = arch_index; std::forward(loadcmd_func)(data); MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); @@ -173,7 +175,7 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, template void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int arch_index, Func&& loadcmd_func) { uint32_t magic = ReadMagic(macho_file); switch (magic) { case MH_MAGIC: @@ -184,12 +186,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, arch_index, std::forward(loadcmd_func)); break; case MH_MAGIC_64: ParseMachOHeaderImpl( - macho_file, overhead_sink, std::forward(loadcmd_func)); + macho_file, overhead_sink, arch_index, std::forward(loadcmd_func)); break; case MH_CIGAM: case MH_CIGAM_64: @@ -230,7 +232,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)); } } @@ -244,7 +246,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: @@ -492,7 +494,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_arch_index(cmd.arch_index); + ParseLoadCommand(cmd, sink); + }); } template @@ -543,6 +548,7 @@ void ParseSymbols(string_view file_data, SymbolTable* symtab, RangeSink* sink) { ForEachLoadCommand( file_data, sink, [symtab, sink](const LoadCommand& cmd) { + sink->set_arch_index(cmd.arch_index); switch (cmd.cmd) { case LC_SYMTAB: if (cmd.is64bit) { @@ -562,6 +568,7 @@ static void AddMachOFallback(RangeSink* sink) { ForEachLoadCommand( sink->input_file().data(), sink, [sink](const LoadCommand& cmd) { + sink->set_arch_index(cmd.arch_index); switch (cmd.cmd) { case LC_SEGMENT_64: AddSegmentAsFallback( @@ -633,6 +640,9 @@ static void ReadDebugSectionsFromMachO(const InputFile &file, dwarf->open = &ReadDebugSectionsFromMachO; ForEachLoadCommand( file.data(), nullptr, [dwarf, sink](const LoadCommand &cmd) { + if (sink) { + sink->set_arch_index(cmd.arch_index); + } switch (cmd.cmd) { case LC_SEGMENT_64: ReadDebugSectionsFromSegment( @@ -669,6 +679,25 @@ class MachOObjectFile : public ObjectFile { return id; } + // For universal binaries, name each VM address space after its architecture + // slice (e.g. {0: "x86_64", 1: "arm64"}). Single-arch binaries have one + // address space and need no names. + std::map GetArchNames() const override { + std::map names; + if (ReadMagic(file_data().data()) != FAT_CIGAM) { + return names; + } + string_view header_data = file_data().data(); + auto header = GetStructPointerAndAdvance(&header_data); + uint32_t nfat_arch = ByteSwap(header->nfat_arch); + for (uint32_t i = 0; i < nfat_arch; i++) { + auto arch = GetStructPointerAndAdvance(&header_data); + names[i] = CpuTypeToString(ByteSwap(arch->cputype), + ByteSwap(arch->cpusubtype)); + } + return names; + } + void ProcessFile(const std::vector& sinks) const override { for (auto sink : sinks) { switch (sink->data_source()) { @@ -735,12 +764,14 @@ class MachOObjectFile : public ObjectFile { std::string arch_name = CpuTypeToString(cputype, cpusubtype); string_view slice_data = StrictSubstr(file_data().data(), offset, size); + sink->set_arch_index(i); sink->AddFileRange("archs", arch_name, slice_data); } } else { auto header = GetStructPointer(file_data().data()); std::string arch_name = CpuTypeToString(header->cputype, header->cpusubtype); + sink->set_arch_index(0); sink->AddFileRange("archs", arch_name, file_data().data()); } } diff --git a/tests/macho/universal-binary.test b/tests/macho/universal-binary.test new file mode 100644 index 00000000..a726a0fc --- /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