Apples GCC 在哪里/如何将 DWARF 存储在可执行文件中?
gcc -gdwarf-2
我通过(Apples GCC)编译了一个二进制文件。但是,既没有objdump -g
也objdump -h
没有向我显示任何调试信息。
libbfd 也找不到任何调试信息。(我在 binutils-mailinglist 上问过这个问题。)
但是,我可以通过dsymutil
(到 dSYM)提取调试信息。libbfd 也能够读取这些调试信息。
id
在 Mac OS X 上,当您链接程序时,决定让链接器不处理所有调试信息。调试信息通常10x
是程序可执行文件的大小,因此让链接器处理所有调试信息并将其包含在可执行二进制文件中会严重损害链接时间。对于迭代开发——编译、链接、编译、链接、调试、编译链接——这是一个真正的成功。
相反,编译器DWARF
在 .s 文件中生成调试信息,汇编器在 .o 文件中输出它,链接器在可执行二进制文件中包含一个“调试映射”,它告诉调试信息用户所有符号在链接。
消费者(进行 .o 文件调试)从可执行文件加载调试映射并根据需要处理 .o 文件中的所有 DWARF,根据调试映射的说明重新映射符号。
dsymutil
可以被认为是一个调试信息链接器。它执行相同的过程——读取调试映射,从 .o 文件加载 DWARF,重新定位所有地址——然后在最终链接地址处输出所有 DWARF 的单个二进制文件。这是 dSYM 捆绑包。
一旦你有了 dSYM 包,你就有了普通的旧标准 DWARF,任何 dwarf 阅读工具(可以处理 Mach-O 二进制文件)都可以处理。
Mach-O 二进制文件中包含的 UUID 使所有这些工作都得到了额外的改进。每次链接器创建二进制文件时,它都会在 LC_UUID 加载命令(v.otool -hlv
或dwarfdump --uuid
)中发出 128 位 UUID。这唯一地标识了该二进制文件。创建dsymutil
dSYM 时,它包含该 UUID。如果 dSYM 和可执行文件具有匹配的 UUID,调试器只会关联它们——没有不可靠的文件 mod 时间戳或类似的东西。
我们还可以使用 UUID 来定位二进制文件的 dSYM。它们出现在崩溃报告中,我们包括一个 Spotlight 导入器,您可以使用它来搜索它们,例如,
mdfind "com_apple_xcode_dsym_uuids == E21A4165-29D5-35DC-D08D-368476F85EE1"
如果 dSYM 位于 Spotlight 索引位置。您甚至可以为您的公司拥有一个 dSYM 存储库和一个程序,该程序可以在给定 UUID 的情况下检索正确的 dSYM - 可能是一个小的 mysql 数据库或类似的东西 - 所以您在随机可执行文件上运行调试器并立即拥有所有调试该可执行文件的信息。您可以使用 UUID 做一些非常简洁的事情。
但是无论如何,要回答您最初的问题:未剥离的二进制文件具有调试映射,.o 文件具有 DWARF,并且在dsymutil
运行时将它们组合在一起以创建 dSYM 包。
如果您想查看调试映射条目,请执行此操作nm -pa executable
,它们都在那里。它们采用旧的 stabsnlist
记录的形式 - 链接器已经知道如何处理 stabs,因此使用它们最容易 - 但您会毫不费力地看到它是如何工作的,如果您不确定,可以参考一些 stabs 文档.
OSX似乎有两种放置调试信息的方法:
在.o
用于编译的目标文件中。二进制文件存储对这些文件的引用(通过绝对路径)。
在名为 .dSYM 的单独包(目录)中
如果我使用 Apple 的 Clang 进行编译,g++ -g main.cpp -o foo
我会得到一个名为foo.dSYM
. 但是,如果我使用 CMake,我会在目标文件中获得调试信息。我猜是因为它做了一个单独的gcc -c main.cpp -o main.o
步骤?
无论如何,我发现这个命令对于案例 1 非常有用:
$ dsymutil -dump-debug-map main
---
triple: 'x86_64-apple-darwin'
binary-path: main
objects:
- filename: /Users/tim/foo/build/CMakeFiles/main.dir/main.cpp.o
timestamp: 1485951213
symbols:
- { sym: __ZNSt3__111char_traitsIcE11eq_int_typeEii, objAddr: 0x0000000000000D50, binAddr: 0x0000000100001C90, size: 0x00000020 }
- { sym: __ZNSt3__111char_traitsIcE6lengthEPKc, objAddr: 0x0000000000000660, binAddr: 0x00000001000015A0, size: 0x00000020 }
- { sym: GCC_except_table3, objAddr: 0x0000000000000DBC, binAddr: 0x0000000100001E2C, size: 0x00000000 }
- { sym: _main, objAddr: 0x0000000000000000, binAddr: 0x0000000100000F40, size: 0x00000090 }
- { sym: __ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m, objAddr: 0x00000000000001F0, binAddr: 0x0000000100001130, size: 0x00000470 }
- { sym: ___clang_call_terminate, objAddr: 0x0000000000000D40, binAddr: 0x0000000100001C80, size: 0x00000010 }
- { sym: GCC_except_table5, objAddr: 0x0000000000000E6C, binAddr: 0x0000000100001EDC, size: 0x00000000 }
- { sym: __ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_, objAddr: 0x0000000000000680, binAddr: 0x00000001000015C0, size: 0x000006C0 }
- { sym: __ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, objAddr: 0x00000000000000E0, binAddr: 0x0000000100001020, size: 0x00000110 }
- { sym: GCC_except_table2, objAddr: 0x0000000000000D7C, binAddr: 0x0000000100001DEC, size: 0x00000000 }
- { sym: __ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc, objAddr: 0x0000000000000090, binAddr: 0x0000000100000FD0, size: 0x00000050 }
- { sym: __ZNSt3__111char_traitsIcE3eofEv, objAddr: 0x0000000000000D70, binAddr: 0x0000000100001CB0, size: 0x0000000B }
...
Apple 将调试信息存储在名为 *.dSYM 的单独文件中。您可以在这些文件上运行 dwarfdump 并查看 DWARF 调试信息条目。