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



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

char code[] = "1234";
if (atoi(code) == 4321) 
{
puts("Right");
} 
else 
{
puts("Wrong");
}

我用GDB调试程序。

当我逐步完成程序时,有没有办法找出定义为4321的整数的值?

在一般情况下为否,例如if (x < 10)可能被编译为x86的cmp reg, 9/jle(x <= 9(。当然,这是相同的逻辑,但您无法从机器代码中的立即数操作数中恢复源使用的是9还是10。

或者,常量传播或值范围分析可能在编译时使条件为true,因此asm中根本不会出现即时值。例如知道CCD_ 5做什么的智能编译器可以将该C编译为与CCD_。


在这个特定的情况下,事实证明GCC/clang并不需要寻找优化;编译时常量字符串上的atoi不是普通程序经常做的事情,因此不值得在编译时将strtol作为支持常量传播的内置函数。

在这种情况下,当GCC或clang为x86-64 GNU/Linux(Godbolt(编译数字时,数字显然是一个立即常量,您的代码(包括数组声明(位于函数中。不是全球性的;这将使持续传播变得不可能。

这是编译器的asm输出;它没有像您在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 ISAs有时必须使用2条指令来构造常量,或者(在32位ARM上常见(使用PC相对负载从内存中加载常量。所以你不会总是把常数看作一个数字。

最新更新