diff --git a/CMakeLists.txt b/CMakeLists.txt index 452ba3e..05b5289 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,6 +373,7 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src) if(PROTOC_FOUND) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.h DEPENDS protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty.proto COMMAND protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty.proto --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src @@ -381,6 +382,7 @@ add_custom_command( else() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.h COMMAND protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty.proto --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src -I${CMAKE_CURRENT_SOURCE_DIR}/src @@ -395,6 +397,7 @@ add_library(libbloaty STATIC src/bloaty.h src/disassemble.cc ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.h src/dwarf/attr.h src/dwarf/attr.cc src/dwarf/dwarf_util.cc diff --git a/doc/using.md b/doc/using.md index 44a1484..3d3cc0e 100644 --- a/doc/using.md +++ b/doc/using.md @@ -327,6 +327,28 @@ can create with `dsymutil`: ``` $ dsymutil bloaty $ strip bloaty (optional) +$ ./bloaty -d symbols bloaty # Auto-discovers bloaty.dSYM +``` + +Bloaty will automatically discover and load debug symbols from +`bloaty.dSYM/Contents/Resources/DWARF/bloaty` when analyzing `bloaty`. + +If you used `dsymutil -o` to write the dSYM to a different directory, +you can explicitly specify its location: + +``` +$ dsymutil bloaty -o /path/to/symbols/bloaty.dSYM +$ ./bloaty -d symbols --dsym=/path/to/symbols/bloaty.dSYM bloaty +``` + +**Note:** The `--dsym` option is also useful for debugging issues with auto-loading dSYMs. +When you explicitly specify `--dsym`, Bloaty will throw informative error messages +if the dSYM file is invalid or has a mismatched build ID, rather than silently +ignoring the file as it does during auto-discovery. + +For cross-platform compatibility, `--debug-file` still works: + +``` $ ./bloaty -d symbols --debug-file=bloaty.dSYM/Contents/Resources/DWARF/bloaty bloaty ``` diff --git a/src/bloaty.cc b/src/bloaty.cc index d711912..f489c60 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -29,6 +29,7 @@ typedef size_t z_size_t; #include #include +#include #include #include #include @@ -1526,6 +1527,9 @@ class Bloaty { void AddFilename(const std::string& filename, bool base_file); void AddDebugFilename(const std::string& filename); void AddSourceMapFilename(const std::string& filename); + void AddDsymFilename(const Options& options, const std::string& filename); + void TryAutoLoadDsym(const std::string& binary_path, + const std::string& build_id); size_t GetSourceCount() const { return sources_.size(); } @@ -1573,6 +1577,15 @@ class Bloaty { std::unique_ptr GetObjectFile(const std::string& filename) const; + bool TryGetBuildId(const std::string& filename, std::string* build_id); + bool TryValidateAndAddDsym(const std::string& dsym_path, + const std::string& main_binary_path, + bool strict_validation); + + std::filesystem::path ConstructDsymPath(const std::filesystem::path& binary_path); + std::filesystem::path GetDsymInternalPath(const std::filesystem::path& dsym_bundle, + const std::string& binary_name); + const InputFileFactory& file_factory_; const Options options_; @@ -1634,6 +1647,9 @@ void Bloaty::AddFilename(const std::string& filename, bool is_base) { auto object_file = GetObjectFile(filename); std::string build_id = object_file->GetBuildId(); + // Automatically try to load dSYM file for Mach-O binaries + TryAutoLoadDsym(filename, build_id); + if (is_base) { base_files_.push_back({filename, build_id}); } else { @@ -1663,6 +1679,137 @@ void Bloaty::AddSourceMapFilename(const std::string& filename) { sourcemap_files_[sourcemap_build_id] = sourcemap_filename; } +bool Bloaty::TryGetBuildId(const std::string& filename, std::string* build_id) { + try { + auto object_file = GetObjectFile(filename); + *build_id = object_file->GetBuildId(); + return true; + } catch (const std::exception&) { + return false; + } +} + +bool Bloaty::TryValidateAndAddDsym(const std::string& dsym_path, + const std::string& main_binary_path, + bool strict_validation) { + std::string main_build_id, dsym_build_id; + + if (!TryGetBuildId(main_binary_path, &main_build_id) || + !TryGetBuildId(dsym_path, &dsym_build_id)) { + if (strict_validation) { + THROWF("Cannot read build ID from files: main=$0, dsym=$1", + main_binary_path, dsym_path); + } + return false; + } + + if (main_build_id != dsym_build_id) { + if (strict_validation) { + THROWF("dSYM file $0 has mismatched build ID (expected $1, got $2)", + dsym_path, + absl::BytesToHexString(main_build_id), + absl::BytesToHexString(dsym_build_id)); + } else { + WARN("dSYM file $0 has mismatched build ID (expected $1, got $2) - skipping", + dsym_path, + absl::BytesToHexString(main_build_id), + absl::BytesToHexString(dsym_build_id)); + } + return false; + } + + AddDebugFilename(dsym_path); + return true; +} + +std::filesystem::path Bloaty::ConstructDsymPath(const std::filesystem::path& binary_path) { + std::filesystem::path binary_file(binary_path); + std::string binary_name = binary_file.filename().string(); + std::filesystem::path binary_dir = binary_file.parent_path(); + + std::filesystem::path dsym_bundle = binary_dir / (binary_name + ".dSYM"); + return dsym_bundle / "Contents" / "Resources" / "DWARF" / binary_name; +} + +std::filesystem::path Bloaty::GetDsymInternalPath(const std::filesystem::path& dsym_bundle, + const std::string& binary_name) { + return dsym_bundle / "Contents" / "Resources" / "DWARF" / binary_name; +} + +// Loads dSYM files explicitly specified by the user using the --dsym flag. +// +// This function processes dSYM bundle paths with strict validation: +// - Warns if no dSYM files match the input binaries +// - Throws if the dSYM bundle path doesn't exist or isn't a directory +// - Throws if dSYM files are invalid or unreadable +// - Throws if buildids don't match between binary and dSYM +// +// The strict behaviour is intentional: when users explicitly specify --dsym, +// they expect it to work and want to know immediately if there are problems. +void Bloaty::AddDsymFilename(const Options& options, + const std::string& filename) { + if (!std::filesystem::exists(filename)) { + THROWF("couldn't open '$0'", filename.c_str()); + } + + std::filesystem::path dsym_bundle(filename); + // Only support dSYM bundles + if (std::filesystem::is_directory(dsym_bundle)) { + bool found_any = false; + for (const auto& binary_filename : options.filename()) { + std::filesystem::path binary_name = std::filesystem::path(binary_filename).filename(); + std::filesystem::path dsym_file = GetDsymInternalPath(dsym_bundle, binary_name.string()); + + if (std::filesystem::exists(dsym_file)) { + if (TryValidateAndAddDsym(dsym_file.string(), binary_filename, + /*strict_validation=*/true)) { + found_any = true; + } + } + } + if (!found_any) { + WARN("dSYM bundle '$0' contains no debug files matching input binaries", + filename); + } + } else { + THROWF("'$0' is not a dSYM bundle directory", filename); + } +} + +// Automatically discovers and loads dSYM files using macOS conventions. +// +// This function attempts to find dSYM bundles using the standard macOS layout: +// /.dSYM/Contents/Resources/DWARF/ +// +// Key behavioural differences from AddDsymFilename(): +// - Never throws exceptions or breaks analysis if discovery fails +// - Ignores invalid/corrupted dSYM files +// - Warns on buildid mismatches but continues without the dSYM +// +// The permissive behaviour is intentional because auto-loading is speculative and +// shouldn't break analysis of the main binary. Users didn't explicitly +// request the dSYM so failures should be silent. +void Bloaty::TryAutoLoadDsym(const std::string& binary_path, + const std::string& build_id) { + if (build_id.empty()) { + return; + } + + std::filesystem::path dsym_file = ConstructDsymPath(binary_path); + + if (std::filesystem::exists(dsym_file)) { + // Use non-strict validation because auto-loading is speculative + // GetObjectFile() will throw if the file isn't a valid Mach-O and + // we want auto-loading to silently fail rather than break analysis. + if (TryValidateAndAddDsym(dsym_file.string(), binary_path, + /*strict_validation=*/false)) { + if (verbose_level > 1) { + printf("Found matching dSYM file: %s\n", dsym_file.string().c_str()); + } + } + } +} + void Bloaty::DefineCustomDataSource(const CustomDataSource& source) { if (source.base_data_source() == "symbols") { THROW( @@ -2030,6 +2177,7 @@ USAGE: bloaty [OPTION]... FILE... [-- BASE_FILE...] -c FILE Load configuration from . -d SOURCE,SOURCE Comma-separated list of sources to scan. --debug-file=FILE Use this file for debug symbols and/or symbol table. + --dsym=FILE Use this dSYM file or bundle (Mach-O only). --source-map=ID=FILE Use this source map file for the binary. The ID can be the build ID (or Wasm sourceMappingURL) or the file path @@ -2224,6 +2372,8 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], } } else if (args.TryParseOption("--debug-file", &option)) { options->add_debug_filename(std::string(option)); + } else if (args.TryParseOption("--dsym", &option)) { + options->add_dsym_path(std::string(option)); } else if (args.TryParseUint64Option("--debug-fileoff", &uint64_option)) { if (options->has_debug_fileoff()) { THROW("currently we only support a single debug fileoff"); @@ -2349,6 +2499,8 @@ void BloatyDoMain(const Options& options, const InputFileFactory& file_factory, THROW("max_rows_per_level must be at least 1"); } + verbose_level = options.verbose_level(); + for (auto& filename : options.filename()) { bloaty.AddFilename(filename, false); } @@ -2361,6 +2513,10 @@ void BloatyDoMain(const Options& options, const InputFileFactory& file_factory, bloaty.AddDebugFilename(debug_filename); } + for (const auto& dsym_path : options.dsym_path()) { + bloaty.AddDsymFilename(options, dsym_path); + } + for (auto& sourcemap : options.source_map()) { bloaty.AddSourceMapFilename(sourcemap); } @@ -2380,8 +2536,6 @@ void BloatyDoMain(const Options& options, const InputFileFactory& file_factory, } } - verbose_level = options.verbose_level(); - if (options.data_source_size() > 0) { bloaty.ScanAndRollup(options, output); } else if (options.has_disassemble_function()) { diff --git a/src/bloaty.proto b/src/bloaty.proto index 5eb4421..0c1e87a 100644 --- a/src/bloaty.proto +++ b/src/bloaty.proto @@ -32,6 +32,9 @@ message Options { // Build id to source map file names, delimited by '='. repeated string source_map = 15; + // Mach-O specific option to specify path to a dSYM file. + repeated string dsym_path = 16; + // The data sources to scan in each file. At least one data source must be // specified. If more than one source is specified, the output is // hierarchical. diff --git a/tests/macho/dsym-loading.test b/tests/macho/dsym-loading.test new file mode 100644 index 0000000..f10d773 --- /dev/null +++ b/tests/macho/dsym-loading.test @@ -0,0 +1,798 @@ +# Test that bloaty can load debug info from dSYM bundles +# +# This test verifies bloaty's dSYM support: +# 1. The --debug-file option can load dSYM files directly +# 2. The --dsym option can load dSYM bundles +# 3. Auto-loading finds .dSYM/Contents/Resources/DWARF/ +# +# The YAML below was generated from tests/testdata/macho/test_dsym.c. +# +# On macOS: +# clang -g -o test_binary tests/testdata/macho/test_dsym.c +# obj2yaml test_binary > binary.yaml +# obj2yaml test_binary.dSYM/Contents/Resources/DWARF/test_binary > dsym.yaml +# +# Then manually modified to work around yaml2obj roundtrip bug +# https://github.com/llvm/llvm-project/issues/166993 +# - Removed Length field from debug_aranges and debug_line sections +# - Manually adjusted __DWARF segment size and section offsets to match actual content sizes + +# Test 1: Explicitly specify dSYM with --debug-file option +# RUN: rm -rf %t && mkdir -p %t +# RUN: %yaml2obj --docnum=1 %s -o %t/test_binary +# RUN: %yaml2obj --docnum=2 %s -o %t/test_dsym +# RUN: %bloaty %t/test_binary --debug-file=%t/test_dsym -d compileunits --domain=vm | %FileCheck %s + +# Test 2: Explicit dSYM bundle with --dsym flag +# RUN: mkdir -p %t/test_binary.dSYM/Contents/Resources/DWARF +# RUN: cp %t/test_dsym %t/test_binary.dSYM/Contents/Resources/DWARF/test_binary +# RUN: %bloaty %t/test_binary --dsym=%t/test_binary.dSYM -d compileunits --domain=vm | %FileCheck %s + +# Test 3: Test auto-loading (no explicit flags, dSYM bundle in standard location) +# RUN: %bloaty %t/test_binary -d compileunits --domain=vm | %FileCheck %s + +# CHECK: VM SIZE +# CHECK: test_dsym.c + +## Minimal Mach-O executable +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x100000C + cpusubtype: 0x0 + filetype: 0x2 + ncmds: 16 + sizeofcmds: 744 + 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: 808 + 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: 56 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 16504 + nsyms: 16 + stroff: 16760 + strsize: 59 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 13 + iextdefsym: 13 + nextdefsym: 3 + iundefsym: 16 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + Content: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: 6E7ACE10-947B-3AB7-A4B1-2D5BB81AE668 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 983040 + sdk: 1703936 + ntools: 1 + Tools: + - tool: 3 + version: 80020480 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 816 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 88866816 + compatibility_version: 65536 + Content: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 16496 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 16504 + datasize: 0 + - cmd: LC_CODE_SIGNATURE + cmdsize: 16 + dataoff: 16912 + datasize: 280 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 23 + Name: _ + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 9 + Name: _mh_execute_header + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 13 + Name: foo + Flags: 0x0 + Address: 0x328 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 18 + Name: main + Flags: 0x0 + Address: 0x330 + Other: 0x0 + ImportName: '' + NameList: + - n_strx: 1 + n_type: 0x64 + n_sect: 1 + n_desc: 0 + n_value: 0 + - n_strx: 33 + n_type: 0x64 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 35 + n_type: 0x64 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 47 + n_type: 0x66 + n_sect: 0 + n_desc: 1 + n_value: 1762531483 + - n_strx: 1 + n_type: 0x2E + n_sect: 1 + n_desc: 0 + n_value: 4294968104 + - n_strx: 22 + n_type: 0x24 + n_sect: 1 + n_desc: 0 + n_value: 4294968104 + - n_strx: 1 + n_type: 0x24 + n_sect: 0 + n_desc: 0 + n_value: 8 + - n_strx: 1 + n_type: 0x4E + n_sect: 1 + n_desc: 0 + n_value: 4294968104 + - n_strx: 1 + n_type: 0x2E + n_sect: 1 + n_desc: 0 + n_value: 4294968112 + - n_strx: 27 + n_type: 0x24 + n_sect: 1 + n_desc: 0 + n_value: 4294968112 + - n_strx: 1 + n_type: 0x24 + n_sect: 0 + n_desc: 0 + n_value: 40 + - n_strx: 1 + n_type: 0x4E + n_sect: 1 + n_desc: 0 + n_value: 4294968112 + - n_strx: 1 + n_type: 0x64 + n_sect: 1 + n_desc: 0 + n_value: 0 + - n_strx: 2 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968104 + - n_strx: 27 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968112 + StringTable: + - ' ' + - __mh_execute_header + - _foo + - _main + - '/' + - test_dsym.c + - 'test_dsym.o' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + FunctionStarts: [ 0x328, 0x330 ] + ChainedFixups: [ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x30, 0x0, + 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ] +... + +## dSYM file with DWARF debug info +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x100000C + cpusubtype: 0x0 + filetype: 0xA + ncmds: 7 + sizeofcmds: 1240 + flags: 0x0 + reserved: 0x0 +LoadCommands: + - cmd: LC_UUID + cmdsize: 24 + uuid: 6E7ACE10-947B-3AB7-A4B1-2D5BB81AE668 + - cmd: LC_BUILD_VERSION + cmdsize: 24 + platform: 1 + minos: 983040 + sdk: 1703936 + ntools: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 4096 + nsyms: 3 + stroff: 4144 + strsize: 33 + - 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: 0 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100000328 + size: 48 + offset: 0x0 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: CFFAEDFE0C000001000000000A00000007000000D804000000000000000000001B000000180000006E7ACE10947B3AB7 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100000358 + size: 96 + offset: 0x0 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: CFFAEDFE0C000001000000000A00000007000000D804000000000000000000001B000000180000006E7ACE10947B3AB7A4B12D5BB81AE66832000000180000000100000000000F0000001A000000000002000000180000000010000003000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294983680 + vmsize: 4096 + fileoff: 4096 + filesize: 81 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 792 + segname: __DWARF + vmaddr: 4294987776 + vmsize: 4096 + fileoff: 8192 + filesize: 673 + maxprot: 7 + initprot: 3 + nsects: 9 + flags: 0 + Sections: + - sectname: __debug_line + segname: __DWARF + addr: 0x100005000 + size: 85 + offset: 0x2000 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __debug_aranges + segname: __DWARF + addr: 0x100005055 + size: 48 + offset: 0x2055 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __debug_info + segname: __DWARF + addr: 0x100005085 + size: 119 + offset: 0x2085 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __debug_abbrev + segname: __DWARF + addr: 0x1000050FC + size: 98 + offset: 0x20FC + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __debug_str + segname: __DWARF + addr: 0x10000515E + size: 84 + offset: 0x215E + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __apple_namespac + segname: __DWARF + addr: 0x1000051B2 + size: 36 + offset: 0x21B2 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 485341480100000001000000000000000C000000000000000100000001000600FFFFFFFF + - sectname: __apple_names + segname: __DWARF + addr: 0x1000051D6 + size: 88 + offset: 0x21D6 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 485341480100000002000000020000000C00000000000000010000000100060000000000010000006A7F9A7C8973880B38000000480000004400000001000000470000000000000040000000010000002E00000000000000 + - sectname: __apple_types + segname: __DWARF + addr: 0x10000522E + size: 79 + offset: 0x222E + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 48534148010000000100000001000000180000000000000004000000010006000300050005000B0006000600000000003080880B3800000050000000010000006F000000240000A4283A0C00000000 + - sectname: __apple_objc + segname: __DWARF + addr: 0x10000527D + size: 36 + offset: 0x227D + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 485341480100000001000000000000000C000000000000000100000001000600FFFFFFFF +LinkEditData: + NameList: + - n_strx: 2 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968104 + - n_strx: 27 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968112 + StringTable: + - '' + - '' + - __mh_execute_header + - _foo + - _main +DWARF: + debug_str: + - '' + - 'Apple clang version 17.0.0 (clang-1700.3.19.1)' + - test_dsym.c + - '/' + - . + - foo + - main + - result + - int + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_producer + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_LLVM_sysroot + Form: DW_FORM_strp + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Attribute: DW_AT_comp_dir + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Attribute: DW_AT_APPLE_omit_frame_ptr + Form: DW_FORM_flag_present + - Attribute: DW_AT_frame_base + Form: DW_FORM_exprloc + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_prototyped + Form: DW_FORM_flag_present + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Attribute: DW_AT_frame_base + Form: DW_FORM_exprloc + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_prototyped + Form: DW_FORM_flag_present + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x4 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_exprloc + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x5 + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_encoding + Form: DW_FORM_data1 + - Attribute: DW_AT_byte_size + Form: DW_FORM_data1 + debug_aranges: + - Length: 0x2C + Version: 2 + CuOffset: 0x0 + AddressSize: 0x8 + Descriptors: + - Address: 0x100000328 + Length: 0x30 + debug_info: + - Length: 0x73 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x1D + - Value: 0x30 + - Value: 0x3C + - Value: 0x0 + - Value: 0x3E + - Value: 0x100000328 + - Value: 0x30 + - AbbrCode: 0x2 + Values: + - Value: 0x100000328 + - Value: 0x8 + - Value: 0x1 + - Value: 0x1 + BlockData: [ 0x6F ] + - Value: 0x40 + - Value: 0x1 + - Value: 0x5 + - Value: 0x1 + - Value: 0x6F + - Value: 0x1 + - AbbrCode: 0x3 + Values: + - Value: 0x100000330 + - Value: 0x28 + - Value: 0x1 + BlockData: [ 0x6D ] + - Value: 0x44 + - Value: 0x1 + - Value: 0x7 + - Value: 0x1 + - Value: 0x6F + - Value: 0x1 + - AbbrCode: 0x4 + Values: + - Value: 0x2 + BlockData: [ 0x8F, 0x8 ] + - Value: 0x49 + - Value: 0x1 + - Value: 0x8 + - Value: 0x6F + - AbbrCode: 0x0 + - AbbrCode: 0x5 + Values: + - Value: 0x50 + - Value: 0x5 + - Value: 0x4 + - AbbrCode: 0x0 + debug_line: + - Length: 81 + Version: 4 + PrologueLength: 35 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + Files: + - Name: test_dsym.c + DirIdx: 0 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4294968104 + - Opcode: DW_LNS_set_column + Data: 17 + - Opcode: DW_LNS_set_prologue_end + Data: 0 + - Opcode: 0x16 + Data: 0 + - Opcode: DW_LNS_set_column + Data: 0 + - Opcode: 0x84 + Data: 0 + - Opcode: DW_LNS_set_column + Data: 16 + - Opcode: DW_LNS_set_prologue_end + Data: 0 + - Opcode: 0xF3 + Data: 0 + - Opcode: DW_LNS_set_column + Data: 7 + - Opcode: DW_LNS_negate_stmt + Data: 0 + - Opcode: 0x4A + Data: 0 + - Opcode: DW_LNS_set_column + Data: 10 + - Opcode: DW_LNS_negate_stmt + Data: 0 + - Opcode: 0x4B + Data: 0 + - Opcode: DW_LNS_set_column + Data: 3 + - Opcode: DW_LNS_negate_stmt + Data: 0 + - Opcode: DW_LNS_set_epilogue_begin + Data: 0 + - Opcode: 0x4A + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 12 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 +... diff --git a/tests/macho/no-dsym.test b/tests/macho/no-dsym.test new file mode 100644 index 0000000..469f958 --- /dev/null +++ b/tests/macho/no-dsym.test @@ -0,0 +1,247 @@ +# Test that bloaty handles missing dSYM gracefully +# +# When a dSYM bundle doesn't exist, bloaty should continue without error. +# +# The YAML below was generated from tests/testdata/macho/test_dsym.c. +# +# On macOS: +# clang -g -O2 -o test_binary tests/testdata/macho/test_dsym.c +# strip -S test_binary +# obj2yaml test_binary > binary.yaml + +# RUN: %yaml2obj %s -o %t.binary +# RUN: %bloaty %t.binary -d segments --domain=vm | %FileCheck %s + +# Should still work, just without compile unit info +# CHECK: VM SIZE +# CHECK: __LINKEDIT +# CHECK: __TEXT + +## Minimal Mach-O executable +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x100000C + cpusubtype: 0x0 + filetype: 0x2 + ncmds: 15 + sizeofcmds: 760 + 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: 0x100000348 + size: 72 + offset: 0x348 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 00781F53C0035FD6F44FBEA9FD7B01A9FD43009160008052FAFFFF97F30300AA40008052F7FFFF970804130B13390011E00313AAF3FFFF976002000BFD7B41A9F44FC2A8C0035FD6 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100000390 + size: 96 + offset: 0x390 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 010000001C000000000000001C000000000000001C00000002000000480300004000000040000000900300000000000040000000000000000000000000000000030000000C000200140002000000000008000001000000020100000400000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294983680 + vmsize: 16384 + fileoff: 16384 + filesize: 488 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 16384 + export_size: 72 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 16464 + nsyms: 4 + stroff: 16528 + strsize: 64 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 3 + iundefsym: 3 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + Content: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: DBCBC096-0980-3DA0-AC54-F36396C8B8F3 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 720896 + sdk: 1703936 + ntools: 1 + Tools: + - tool: 3 + version: 80020480 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 848 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 88866816 + compatibility_version: 65536 + Content: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 16456 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 16464 + datasize: 0 + - cmd: LC_CODE_SIGNATURE + cmdsize: 16 + dataoff: 16592 + datasize: 280 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 23 + Name: _ + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 9 + Name: _mh_execute_header + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 13 + Name: external_call + Flags: 0x0 + Address: 0x348 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 18 + Name: main + Flags: 0x0 + Address: 0x350 + Other: 0x0 + ImportName: '' + NameList: + - n_strx: 4 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 24 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968136 + - n_strx: 39 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294968144 + - n_strx: 45 + n_type: 0x1 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - '' + - '' + - '' + - '' + - __mh_execute_header + - _external_call + - _main + - dyld_stub_binder + - '' + - '' + FunctionStarts: [ 0x348, 0x350 ] +... diff --git a/tests/testdata/macho/test_dsym.c b/tests/testdata/macho/test_dsym.c new file mode 100644 index 0000000..4aee22e --- /dev/null +++ b/tests/testdata/macho/test_dsym.c @@ -0,0 +1,9 @@ +// Test file for dSYM generation tests +// Used to generate YAML test cases in tests/macho/dsym-*.test + +int foo() { return 42; } + +int main() { + int result = foo(); + return result; +}