【发布时间】:2019-06-11 06:18:34
【问题描述】:
我正在尝试学习汇编,还有一些我不完全理解其目的的说明。
C 代码
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Argument One - %s\n", argv[1]);
return 0;
}
组装
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 10, 14
.intel_syntax noprefix
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
## %bb.0:
push rbp
mov rbp, rsp
sub rsp, 32
lea rax, [rip + L_.str]
mov dword ptr [rbp - 4], 0
mov dword ptr [rbp - 8], edi
mov qword ptr [rbp - 16], rsi
mov rsi, qword ptr [rbp - 16]
mov rsi, qword ptr [rsi + 8]
mov rdi, rax
mov al, 0
call _printf
xor ecx, ecx
mov dword ptr [rbp - 20], eax ## 4-byte Spill
mov eax, ecx
add rsp, 32
pop rbp
ret
## -- End function
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "Argument One - %s\n"
.subsections_via_symbols
第一季度。 sub rsp, 32
没有局部变量时,为什么要分配 32 个字节的空间?我相信 argc 和 argv 分别保存在寄存器 edi 和 rsi 中。如果它可以将它们移动到堆栈上,那不是只需要 12 个字节吗?
第二季度。 lea rax, [rip + L_.str] 和 mov rdi, rax
我是否正确理解 L_.str 具有字符串 ""Argument One - %s\n" 的地址?据我了解,printf 通过寄存器 rdi 访问该字符串。那么,为什么指令 mov rdi, L_.str 不工作吗?
第三季度。 mov dword ptr [rbp - 4], 0
为什么将零压入堆栈?
第四季度。 mov dword ptr [rbp - 8], edi 和 mov qword ptr [rbp - 16], rsi
我相信这些指令是将 argc 和 argv 放入堆栈。使用 edi 和 rsi 是纯粹的约定吗?
Q5。 mov dword ptr [rbp - 20], eax
我不知道这是做什么的。
【问题讨论】:
-
其中大部分是来自未优化代码的噪音和开销,例如无缘无故地将 args 从寄存器复制到堆栈,并且 (Q5) 将未使用的 printf 返回值溢出到堆栈空间。使用
-O3或-O2编译以获得有趣的部分。 How to remove "noise" from GCC/clang assembly output? -
是的,有一个标准指定如何将参数传递给函数,因此编译器可以生成可以调用其他编译器代码的代码。在您的情况下,它是 x86-64 System V ABI。请参阅What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 和What registers are preserved through a linux x86-64 function call 的函数调用部分。有关更多文档链接,另请参阅 stackoverflow.com/tags/x86/info。
-
您正在编译而没有优化。这会导致编译器生成大量无用的指令。至少通过
-O1,更好的是-O2,这样编译器才能生成合理的代码。