【发布时间】:2025-11-28 18:15:02
【问题描述】:
这是一个将字符串作为输入传递的程序。
我对下面显示的汇编代码感到困惑,特别是第 6 行。 这是我从研究中了解到的:
-
rbp-48是指向存储argv的堆栈地址的指针。 (argv本身,就是指向argv数组开头的地址) - 现在 rax 寄存器存储
argv数组地址。 - 然后我们将 8 个字节添加到 rax。这意味着 rax 现在指向
argv[1]的地址。 (我知道argv[1]中存储了另一个地址,指向一个字符串)。 - 然后我们访问存储在 argv[1] 中的值并将其存储在 rdx 寄存器中。这意味着,rdx 现在指向字符串开始的地址。
- 然后我们将 [rbp-24] = i 计数器变量移动到 eax 寄存器。
- 然后我们有一个行动 cdqe,我认为它不相关。
现在我是否感到困惑:如果我想访问argv[1] 中的第一个字符并将其存储在 eax 寄存器中,我希望汇编程序执行以下操作:
mov eax, BYTE PTR [rdx]
如果我需要访问存储在 argv[1] 中的第二个字符并将其存储在 eax 寄存器中,我希望汇编器执行以下操作:
mov eax, BYTE PTR [rdx+1]
但相反,我看到编译器执行以下操作:
add rax, rdx
- 将内存中字符串开始的地址添加到内存中存储指向字符串开头的地址的地址,并将此结果保存在 rax 中。
我无法理解这条指令如何让 rax 指向 argv[1] 中的任何字符。
下面是循环指令对应的C代码和汇编代码:
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int sum = 0;
for(int i = 0; i < strlen(argv[1]); i ++){
sum += (int)argv[1][i];
}
return 0;
}
组装
mov rax, QWORD PTR [rbp-48]
add rax, 8
mov rdx, QWORD PTR [rax]
mov eax, DWORD PTR [rbp-24]
cdqe
add rax, rdx
movzx eax, BYTE PTR [rax]
movsx eax, al
add DWORD PTR [rbp-20], eax
add DWORD PTR [rbp-24], 1
【问题讨论】:
-
assembly 也必须与架构一起使用。并且汇编器不会给你
add rax, rdx,它是发出该指令的编译器。汇编器只是将人类可读的指令组装成机器代码 -
你不明白如何将 i 添加到 argv[1] 得到 &argv[1][i]?
-
我不明白这种混淆。你知道rdx+1是argv[1][1]的地址,你知道rax包含i,所以应该清楚rdx+rax就是argv[1][i]的地址。下一条语句
movszx eax, byte ptr [rax]加载该地址处的字符。 -
编译时启用优化以获得更易于理解的代码。就像
gcc -Og将变量保存在寄存器中并可能进行轻微优化,或者gcc -O3 -fno-tree-vectorize或gcc -Os。见How to remove "noise" from GCC/clang assembly output?。此代码不将1添加到指针,而是将其添加到双字i,并使用i作为索引。奇怪的是您的编译器没有使用像movzx eax, byte ptr [rax+rdx]这样的索引寻址模式。这是来自 MSVC 的吗?看起来不像反优化的gcc -O0会做什么。 -
代码不完整,应该至少有一条
jmp-family指令,大概2条。