使用Godbolt进入标准库调用

use*_*717 7 c++ assembly

我想知道各种编译器是如何实现的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
Run Code Online (Sandbox Code Playgroud)

这不是很有帮助。如何进入_M_getval()通话并检查那里的装配体?

Pet*_*des 7

您不能“介入”功能;Godbolt不是调试器,而是反汇编程序。您的程序不会运行,只会被编译。(并且除非选择“二进制”输出选项,否则它只会编译为asm,不会编译为机器代码,并且实际上不会链接。)

但是,无论使用什么术语,都不可以,您无法让Godbolt向您展示正在安装的任何版本库的反汇编。

在桌面上单步执行程序。 (使用编译,gcc -O3 -fno-plt以避免必须逐步执行PLT惰性动态链接。)

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


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

不能保证您看到的库代码将与您桌面上的内容或任何其他内容匹配。

但是,实际上它确实安装了x86-64 Linux库,因此从理论上讲,Godbolt可能会为您提供查找和反汇编某些库函数的选项,但是该功能目前不存在。并且仅适用于“ binary”选项可用的目标;我认为对于大多数交叉编译目标而言,它仅具有标头,而没有库。也许还有其他原因导致它无法针对非x86 ISA进行链接和反汇编。


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

我尝试使用进行编译-static -fno-plt -fno-exceptions -fno-rtti -nostartfiles -O3 -march=skylake(因此rdrand和rdseed可以内联;如果它们没有内联,它们将可用)。 -fno-plt对于来说是多余的-static,但是在消除混乱的情况下很有用。

-static使库代码实际出现在Godbolt反汇编的链接二进制文件中但是输出限制为500行,并且std::random_device::_M_getval()偶然的定义不在文件的开头。

-nostartfiles避免_startCRT启动文件中的二进制文件混乱等等。我认为Godbolt已经将它们从反汇编中过滤掉了,因为您没有在正常的二进制输出中看到它们(没有-static)。您不需要运行该程序,因此链接器找不到_start符号也没有关系,而默认情况下只是将ELF入口点放在本.text节的开头。

尽管进行了编译-fno-exceptions -fno-rtti(因此不包含函数的展开处理程序),但libstdc ++函数是在启用异常处理的情况下编译的。因此,将它们链接会带来大量异常代码。静态可执行文件以诸如std::__throw_bad_exception():和的函数定义开始。std::__throw_bad_alloc():

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

不幸的是,我认为.text.coldor .init部分先被链接了,所以在前500行中看不到任何有趣的功能。


即使这可行,也只是二进制模式的反汇编,而不是编译器的asm

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

而且,由于存在许多分支,因此很难遵循复杂的逻辑可能性。运行时单步自动遵循实际的执行路径。