如何在使用 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 加载从内存中加载它们。所以你不会总是发现常量是一个数字。
假设我有一个已编译的二进制程序,没有调试符号,源代码类似于 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");
.
在这个具体案例中,事实证明 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 加载从内存中加载它们。所以你不会总是发现常量是一个数字。