探究Linux下参数传递及查看和修改方法

X86-64下有16个64位寄存器,其中%rdi、%rsi、%rdx,%rcx、%r8、%r9用作传递函数参数,分别对应第1个参数、第2个参数直到第6个参数,如下图所示(图片来自网络):

探究Linux下参数传递及查看和修改方法

如果函数的参数个数超过6个,则超过的参数直接使用栈来传递。在被调用函数执行前,会先将寄存器中的参数压入堆栈,之后的访问会通过栈寄存器加上偏移位置来访问。下面我们结合程序及其反汇编的结果来看一看。

相关阅读:MIPS架构UBOOT和Linux参数传递

C语言程序如下所示:
#include <stdio.h>
#include <stdlib.h>

static int func2(int i,int j)
{
int k;

k = i + j;
return k;
}

static int func(int fd,const char *ptr, int arg3, int arg4,int arg5,
int arg6, int arg7, int arg8)
{
int ret;

ret = arg7 + arg8;

func2(fd, arg3);

return ret;
}

int main(void)
{
func(12, "Hello,World!",3, 4, 5, 6, 7,8);

return 0;
}
  将上述程序保存为m.c,使用gcc加上-g选项编译,然后使用gdb来进行调试,我们在main调用func的位置及func()和func2()函数三处加上断点,如下所示:
(gdb) b m.c:26
Breakpoint 1 at 0x4004d4: file m.c, line26.
(gdb) b func
Breakpoint 2 at 0x4004ac: file m.c, line17.
(gdb) b func2
Breakpoint 3 at 0x40047e: file m.c, line8.
(gdb)
  然后我们在第一个断点处停下,反汇编当前的main函数,查看参数传递方式,如下所示:
(gdb) disassemble /m main
Dump of assembler code for function main:
25 {
0x00000000004004cc <+0>: push%rbp
0x00000000004004cd <+1>: mov%rsp,%rbp
0x00000000004004d0 <+4>: sub $0x10,%rsp

26 func(12,"Hello,World!", 3,4, 5, 6, 7, 8);
=> 0x00000000004004d4<+8>: movl $0x8,0x8(%rsp)
0x00000000004004dc <+16>: movl $0x7,(%rsp)
0x00000000004004e3 <+23>: mov $0x6,%r9d
0x00000000004004e9 <+29>: mov $0x5,%r8d
0x00000000004004ef <+35>: mov $0x4,%ecx
0x00000000004004f4 <+40>: mov $0x3,%edx
0x00000000004004f9 <+45>: mov $0x400608,%esi
0x00000000004004fe <+50>: mov $0xc,%edi
0x0000000000400503 <+55>: callq 0x40048f<func>

27
28 return 0;
0x0000000000400508 <+60>: mov $0x0,%eax

29 }
0x000000000040050d <+65>: leaveq
0x000000000040050e <+66>: retq

End of assembler dump.
(gdb)
  在func(12, "Hello,World!", 3, 4, 5, 6, 7, 8);这行下面我们可以看到在使用callq指令调用func()函数之前,会使用mov指令将前6个参数的值分别保存在edi、esi、edx、ecx、r8d、r9d这个6个寄存器中,而将第7个和第8个参数存储在栈上。这个结果和我们前面说的一致。

linux

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/1171602ae759d8b231ba5d817c61565e.html