一直没探究一下argc和argv的原理,这次记录一下
使用macOS 10.15 SDK GCC 64位测试
来一段简单的C++程序
为了尽可能的调用argc、argv,设计下列代码
1
2
3
4
5
6
7
8
9
int main(int argc, char const *argv[])
{
int total_params = argc + 1;
for (int i = 0; i < argc; ++i)
{
const char * arr = argv[i];
}
return total_params;
}
反汇编
使用g++ -O0 -fverbose-asm -S进行反汇编
参数说明:
-O0取消优化-fverbose-asm使用C++变量名为汇编变量,不重命名-S只输出汇编文件
以下以一段汇编,加执行完汇编后的栈情况进行演示
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
| -24 | -20 | -16 | -12 | -8 | -4 | rbp |
|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 当前main数据地址 |
此时将%rsp导入%rbp,原有%rbp导入栈
1
int total_params = argc + 1;
movl $0, -4(%rbp)
movl %edi, -8(%rbp)
movq %rsi, -16(%rbp)
movl -8(%rbp), %edi
addl $1, %edi
movl %edi, -20(%rbp)
| -24 | -20 | -16 | -12 | -8 | -4 | rbp |
|---|---|---|---|---|---|---|
| - | total_params(argc+1) | argv基地址1 | argv基地址2 | edi(argc) | 0 | 当前main数据地址 |
- -4(%rbp)为什么置0?(待解决)
- 电脑为64位,故基地址为8字节
1
2
3
4
for (int i = 0; i < argc; ++i)
{
const char * arr = argv[i];
}
LBB0_1: #i<argc ## =>This Inner Loop Header: Depth=1
movl -24(%rbp), %eax
cmpl -8(%rbp), %eax
jge LBB0_4
## %bb.2: const char * arr = argv[i]; ## in Loop: Header=BB0_1 Depth=1
movq -16(%rbp), %rax
movslq -24(%rbp), %rcx # rcx为i的值,即偏移量,一个双字符号扩展后送到一个四字地址
movq (%rax,%rcx,8), %rax # 取出argv[i]的地址
movq %rax, -32(%rbp)
## %bb.3: i++ ## in Loop: Header=BB0_1 Depth=1
movl -24(%rbp), %eax
addl $1, %eax
movl %eax, -24(%rbp)
jmp LBB0_1
LBB0_4:
movl -20(%rbp), %eax
popq %rbp
retq
.cfi_endproc
| -24 | -20 | -16 | -12 | -8 | -4 | rbp |
|---|---|---|---|---|---|---|
| i(0~argc-1) | total_params(argc+1) | argv基地址1 | argv基地址2 | edi(argc) | 0 | 当前main数据地址 |
char*为地址指针,64位系统下的char*指针为64位,8字节
总结
在macOS 64位下,
int argc的值保存在%edi上,之后转移到%rbp-8的地址上
char** argv的值基地址保存在%rsi上,之后转移到%rbp-16的地址上,argv数组中的地址指针(char*)以一个数组的形式保存,地址为(%rsi,%rcx,8)(8为64位系统下,32系统下位4)