Assembly中的Linux 64命令行参数

此说明适用于Linux 32位:当Linux程序开始时,所有指向命令行参数的指针都存储在堆栈中。 参数的数量存储在0(%ebp),程序的名称存储在4(%ebp),参数从8(%ebp)存储。

我需要64位的相同信息。

编辑:我有工作代码示例,显示如何使用argc,argv [0]和argv [1]: http : //cubbi.com/fibonacci/asm.html

 .globl _start
 _开始:
     popq%rcx#这是argc,对于一个参数必须是2
     cmpq $ 2,%rcx
     jne usage_exit
     addq $ 8,%rsp#skip argv [0]
     popq%rsi#get argv [1]
    打电话
 ...
 }

它看起来像参数在堆栈上。 由于这个代码不清楚,我问这个问题。 我的猜测是我可以在rbp中保存rsp,然后使用0(%rbp),8(%rbp),16(%rbp)等访问这些参数。

它看起来像第3.4节“ 流程初始化” ,具体如图3.9所示,在已经提到的System V AMD64 ABI中恰恰描述了你想知道的内容。

尽管接受的答案绰绰有余,但我想作出明确的答复,因为还有其他答案可能会混淆。

最重要的(更多信息见下面的例子):在x86-64中,命令行参数通过堆栈传递:

(%rsp) -> number of arguments 8(%rsp) -> address of the name of the executable 16(%rsp) -> address of the first command line argument (if exists) ... so on ... 

它与x86-64传递的函数参数不同,它使用%rdi%rsi等等。

还有一件事:不应该从C的main功能的逆向工程中推断出行为。 C运行时提供入口点_start ,包装命令行参数和调用main作为一个通用函数。 要看到它,让我们考虑下面的例子。

没有C运行时/ GCC与-nostdlib

我们来检查一下这个简单的x86-64汇编程序,它只是返回42:

 .section .text .globl _start _start: movq $60, %rax #60 -> exit movq $42, %rdi #return 42 syscall #run kernel 

我们用以下方式构建它

 as --64 exit64.s -o exit64.o ld -m elf_x86_64 exit64.o -o exit64 

或与

 gcc -nostdlib exit64.s -o exit64 

在gdb中运行

 ./exit64 first second third 

并停止在_start的断点。 我们来检查寄存器:

 (gdb) info registers ... rsi 0x0 0 rdi 0x0 0 ... 

空空如也。 那堆栈呢?

 (gdb) x/5g $sp 0x7fffffffde40: 4 140737488347650 0x7fffffffde50: 140737488347711 140737488347717 0x7fffffffde60: 140737488347724 

所以堆栈中的第一个元素是4 – 预期的argc 。 接下来的4个值看起来很像指针。 我们来看第二个指针:

 (gdb) print (char[5])*(140737488347711) $1 = "first" 

正如所料,这是第一个命令行参数。

所以有实验证据表明,命令行参数是通过x86-64中的堆栈传递的。 然而,只有阅读ABI (正如接受的答案所暗示的),我们可以肯定的是,情况确实如此。

用C运行时

我们必须稍微改变程序,将_start重命名为main ,因为入口点_start由C运行时提供。

 .section .text .globl main main: movq $60, %rax #60 -> exit movq $42, %rdi #return 42 syscall #run kernel 

我们用(C运行时默认情况下使用)构建它:

 gcc exit64gcc.s -o exit64gcc 

在gdb中运行

 ./exit64gcc first second third 

并停在main的断点处。 什么是堆栈?

 (gdb) x/5g $sp 0x7fffffffdd58: 0x00007ffff7a36f45 0x0000000000000000 0x7fffffffdd68: 0x00007fffffffde38 0x0000000400000000 0x7fffffffdd78: 0x00000000004004ed 

它看起来并不熟悉。 并注册?

 (gdb) info registers ... rsi 0x7fffffffde38 140737488346680 rdi 0x4 4 ... 

我们可以看到rdi包含argc值。 但是如果我们现在检查rsi的指针就会发生奇怪的事情:

 (gdb) print (char[5])*($rsi) $1 = "\211\307???" 

但是等等,C中main函数的第二个参数不是char * ,而是char **也是:

 (gdb) print (unsigned long long [4])*($rsi) $8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721} (gdb) print (char[5])*(140737488347708) $9 = "first" 

现在我们发现我们的参数是通过寄存器传递的,就像x86-64中的一个正常函数一样。

结论:正如我们所看到的,关于在使用C运行时的代码和不运行的代码之间传递命令行参数是不同的。

我相信你需要做的是检查出x86-64 ABI 。 具体来说,我认为你需要看3.2.3参数传递。