【问题标题】:where/how does Apples GCC store DWARF inside an executableApples GCC 在哪里/如何将 DWARF 存储在可执行文件中
【发布时间】:2012-04-20 03:26:03
【问题描述】:

Apple 的 GCC 在哪里/如何将 DWARF 存储在可执行文件中?

我通过gcc -gdwarf-2 (Apples GCC) 编译了一个二进制文件。但是,objdump -gobjdump -h 都没有显示任何调试信息。

libbfd 也没有找到任何调试信息。 (我在 binutils-mailinglist 上询问过 here。)

但是,我可以通过dsymutil(到 dSYM)提取调试信息。 libbfd 也可以读取这些调试信息。

【问题讨论】:

    标签: macos gcc objdump dwarf dsymutil


    【解决方案1】:

    在 Mac OS X 上,我们决定让链接器 id 在链接程序时不处理所有调试信息。调试信息通常是10x程序可执行文件的大小,因此让链接器处理所有调试信息并将其包含在可执行二进制文件中会严重损害链接时间。对于迭代开发 - 编译、链接、编译、链接、调试、编译链接 - 这是一个真正的成功。

    相反,编译器在 .s 文件中生成 DWARF 调试信息,汇编器在 .o 文件中输出它,链接器在可执行二进制文件中包含一个“调试映射”,它告诉调试信息用户在哪里的符号在链接期间被重新定位。

    消费者(进行 .o 文件调试)从可执行文件加载调试映射,并根据需要处理 .o 文件中的所有 DWARF,根据调试映射的说明重新映射符号。

    dsymutil 可以被认为是一个调试信息链接器。它执行相同的过程 - 读取调试映射,从 .o 文件加载 DWARF,重新定位所有地址 - 然后在其最终链接地址处输出所有 DWARF 的单个二进制文件。这是 dSYM 包。

    一旦你有了 dSYM 包,你就有了普通的旧标准 DWARF,任何 dwarf 阅读工具(可以处理 Mach-O 二进制文件)都可以处理。

    还有一个额外的改进使所有这些工作,包括在 Mach-O 二进制文件中的 UUID。每次链接器创建二进制文件时,它都会在 LC_UUID 加载命令(v.otool -hlvdwarfdump --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,它们都在那里。它们采用旧 stabs nlist 记录的形式 - 链接器已经知道如何处理 stabs,因此使用它们是最容易的 - 但您会毫不费力地看到它是如何工作的,如果您可以参考一些 stabs 文档'不确定。

    【讨论】:

    • dsymutil 如何知道 .o 文件的位置?我在手册页中看不到任何选项来告诉它。我还需要编译二进制文件-g3,如果需要,我可以在dsymutil'd 之后将其剥离吗?谢谢。
    • 在用 .o 文件的文件名剥离之前,可执行文件中有“调试映射”条目。 nm -pa binary | grep OSO 将列出它们。它们采用旧的 stabs 调试格式(因为链接器已经知道如何处理该格式)。创建 dSYM 后,您可以将它们从可执行文件中删除。 Mac 平台上不需要使用-g3-g 就足够了。我认为-g3 输出预处理宏信息,但 lldb 在 Mac OS X 上没有读取(而且我不知道 clang 是否会输出它。)
    • 感谢您提供详细而有用的答案。
    • @JasonMolenda 多么好的答案,谢谢!除了你的和this 轶事的文章,我似乎找不到任何正式的文章,你知道有吗?
    • 谢谢。嘿,我也写了你链接到的其他轶事文章。 :) T 从未以任何形式正式记录,因为它的受众太小了。所有相关部分都是开源的(编译器、链接器、调试器),所以当然源代码都是可用的。 DWARF 标准委员会正在研究各种加速表和 .o 文件中的 dwarf-in-file 提案。当一切都解决并标准化时,我想我们会看看我们是否可以将工具过渡到使用它。但这不会在短期内发生。
    【解决方案2】:

    好像没有。

    我跟踪了dsymutil,它读取了所有*.o 文件。 objdump -h 还列出了其中的所有调试信息。

    所以这些信息似乎没有被复制到二进制文件中。


    一些相关的cmet也可以找到here

    【讨论】:

      【解决方案3】:

      OSX 似乎有两种方式来放置调试信息:

      1. 在用于编译的.o 目标文件中。二进制文件存储对这些文件的引用(通过绝对路径)。

      2. 在名为 .dSYM 的单独包(目录)中

      如果我使用 g++ -g main.cpp -o foo 使用 Apple 的 Clang 进行编译,我会得到名为 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 }
      ...
      

      【讨论】:

        【解决方案4】:

        Apple 将调试信息存储在名为 *.dSYM 的单独文件中。您可以在这些文件上运行 dwarfdump 并查看 DWARF 调试信息条目。

        【讨论】:

        • 没有。您可以通过dsymutil创建 dSYM。但我的问题是,调试信息在哪里。 IE。 dsymutil 从哪里得到它。但我已经有了答案(见我自己的答案)。它们实际上不在二进制文件中,二进制文件引用了*.o 文件,这也是dsymutil 从中获取数据的地方。
        • 你的问题很模糊。我将其解释为与调试器中的可执行文件一起使用时存储的调试信息在哪里。
        • 是的,那是我的问题。答案是,它存储在*.o 文件中。当您创建一个 dSYM 时,当然您在 dSYM 中还有另一个副本。但在创建 dSYM 之前,没有 dSYM。这就是我在我的问题中所说的。 (“但是,我可以通过dsymutil 提取调试信息。”)您不会自动获得 dSYM。您必须致电dsymutil。 (我认为,当您使用 Xcode 时,Xcode 会自动执行此操作。)还是我错了?
        猜你喜欢
        • 2014-11-08
        • 1970-01-01
        • 2020-07-14
        • 2023-02-14
        • 2019-02-24
        • 1970-01-01
        • 1970-01-01
        • 2011-06-30
        • 2019-05-15
        相关资源
        最近更新 更多