使用 Godbolt 进入标准库调用

Step into standard library call with godbolt

我想知道各种编译器如何实现std::random_device,所以我把它弹出到godbolt

不幸的是,它唯一说的是

std::random_device::operator()():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    std::random_device::_M_getval()
        leave
        ret

这不是很有帮助。我如何进入 _M_getval() 调用并检查那里的程序集?

您不能“单步执行”函数; Godbolt 不是调试器,它是反汇编器(在“二进制”模式下,否则是编译器 asm-text 输出过滤器/查看器)。你的程序没有 运行,它只是被编译了。 (除非您选择“二进制”输出选项,否则它只会编译为 asm,不会编译为机器代码,而且实际上不会 link。)

但是不管术语如何,不,你不能让 Godbolt 向你展示它恰好安装的任何版本的库的反汇编。

Single-step 桌面上的程序。(使用 gcc -O3 -fno-plt 编译以避免必须逐步执行 PLT 惰性动态 linking。 )

(我做到了,Arch Linux 运行s cpuid 上的 libstdc++ 6.2.1 在 std::random_device 的构造函数中。如果 rdrand 可用, 它在调用 _M_getval() 时使用它。仅通过反汇编来弄清楚这一点会很棘手;函数调用和分支有多个级别,如果没有符号,就很难弄清楚是什么。我的 Skylake有 rdseed 可用,但它没有使用它。是的,正如你评论的那样,那将是一个更好的选择。)


不同的编译器可以从同一个源生成不同版本的库函数,这就是编译器资源管理器存在的要点。不,它没有由下拉列表中的每个编译器编译的单独版本的 libstdc++。

不能保证您看到的库代码与您桌面上的代码或任何东西相匹配。

它确实安装了 x86-64 Linux 库,所以理论上 Godbolt 可以为您提供查找和反汇编某些库函数的选项,但该功能不存在现在。并且仅适用于“二进制”选项可用的目标;我认为对于大多数 cross-compile 目标,它只有 headers 而没有库。或者可能有一些其他原因它不会 link 和反汇编非 x86 ISA。


使用 -static 和二进制模式显示内容,但不是我们想要的。

I tried 使用 -static -fno-plt -fno-exceptions -fno-rtti -nostartfiles -O3 -march=skylake 进行编译(因此 rdrand 和 rdseed 在它们被内联时可用;它们没有)。 -fno-plt-static 是多余的,但它在 没有 的情况下很有用,可以消除混乱。

-static 导致库代码最终出现在 Godbolt 反汇编的 linked 二进制文件中 但是输出限制在500行,而且std::random_device::_M_getval()的定义刚好不在文件开头附近。

-nostartfiles 避免使二进制文件与 CRT 启动文件中的 _start 等混淆。不过,我认为 Godbolt 已经从反汇编中过滤掉了这些,因为您在正常的二进制输出中看不到它们(没有 -static)。你不会运行这个程序,所以linker找不到_start符号并默认将ELF入口点放在.text 部分的开始。

尽管使用 -fno-exceptions -fno-rtti 进行编译(因此不包含函数的展开处理程序),但 libstdc++ 函数在编译时启用了异常处理。因此 linking 它们会引入大量异常代码。静态可执行文件以 std::__throw_bad_exception():std::__throw_bad_alloc():

等函数的定义开始

顺便说一句,没有 -fno-exceptions,还有一个 get_random_seed() [clone .cold]: 定义,我认为它是一个展开处理程序。它不是您实际功能的定义。在静态二进制文件的开头附近是 operator new(unsigned long) [clone .cold]:,我认为它又是 libstdc++ 的 exception-handler 代码。

我认为 .text.cold.init 部分首先被 link 编辑,不幸的是,所以 none 有趣的功能将是在前 500 行可见。


即使这有效,也只是 binary-mode 反汇编,而不是编译器 asm

即使使用调试符号,我们也不知道正在访问哪个结构成员,只能知道寄存器的数字偏移量,因为 objdump 不会填充这些。

并且有很多分支,很难遵循复杂的逻辑可能性。 Single-stepping run-time 自动遵循实际 执行路径。


相关: