如何在使用 GDB 调试 C 程序时找出内联常量的值?

How to find out the value of an inline constant while debugging a C program with GDB?

假设我有一个已编译的二进制程序,没有调试符号,源代码类似于 C:

char code[] = "1234";

if (atoi(code) == 4321) 
{
    puts("Right");
} 
else 
{
    puts("Wrong");
}

我用GDB调试程序

有没有办法在我逐步执行程序时找出定义为 4321 的整数的值?

一般情况下没有,例如对于 x86 (x <= 9),if (x < 10) 可能会编译为 cmp reg, 9 / jle。当然这是相同的逻辑,但是你无法从机器代码中的直接操作数中恢复源是使用 9 还是 10。

或 constant-propagation 或 value-range 分析可能在编译时使条件 known-true 因此根本没有立即值出现在 asm 中。例如知道 atoi 做什么的智能编译器可以像 puts("Right");.

一样编译这个 C

在这个具体案例中,事实证明 GCC / clang 不会费心去寻找那个优化; compile-time-constant 字符串上的 atoi 不是普通程序经常做的事情,因此不值得 compile-time 使 strtol 成为支持 constant-propagation.

在这种情况下,当 GCC 或 clang 为 x86-64 编译它时,数字确实作为一个立即常量出现相当明显 GNU/Linux (Godbolt ),将您的代码(包括数组声明)放在一个函数中。不是全球性的;这将使 constant-propagation 不可能。

这是编译器的汇编输出;它没有像您在 gdb 或其他调试器中看到的那样往返于机器代码和返回,但这将保留除符号名称之外的所有内容。

foo:                                    # @foo
        push    rax                          # reserve 8 bytes for the local array
        mov     byte ptr [rsp + 4], 0        # Silly compiler, could have made the next instruction a qword store of a sign-extended 32-bit immediate to get the 0-termination for free.
        mov     dword ptr [rsp], 875770417   # the ASCII bytes of the "4321" array initializer
        mov     rdi, rsp
        xor     esi, esi
        mov     edx, 10
        call    strtol                      # atoi compiled to strtol(code, NULL, 10)
        cmp     eax, 4321                   # compare retval with immediate constant
        mov     eax, offset .L.str
        mov     edi, offset .L.str.1
        cmove   rdi, rax                    # select which string literal to pass to puts, based on FLAGS from the compare
        pop     rax                        # clear up the stack
        jmp     puts                    # TAILCALL

.section .rodata        # Godbolt filters directives, re-added this one.
.L.str:
        .asciz  "Right"

.L.str.1:
        .asciz  "Wrong"

请注意,RISC ISA 有时必须使用 2 条指令构造常量,或者(在 32 位 ARM 上很常见),使用 PC-relative 加载从内存中加载它们。所以你不会总是发现常量是一个数字。