假设我有一个编译过的二进制程序,没有调试符号,源代码与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相对负载从内存中加载常量。所以你不会总是把常数看作一个数字。