2010-09-10 76 views
5

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

我需要64位的相同信息。

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

 
.globl _start 
_start: 
    popq %rcx  # this is argc, must be 2 for one argument 
    cmpq $2,%rcx 
    jne  usage_exit 
    addq $8,%rsp  # skip argv[0] 
    popq %rsi  # get argv[1] 
    call ... 
... 
} 

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

回答

9

看起来像3.4节过程初始化,特别是图3.9,在已经提到的System V AMD64 ABI中正好描述了你想知道的内容。

+2

链接只有答案是脆弱的。该页面不适合我。 * www.x86-64.org没有发送任何数据。 ERR_EMPTY_RESPONSE * – doug65536 2017-10-30 10:26:40

1

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

+2

这描述了程序内部的调用约定。处理命令行参数可能有自己的规则。我需要这个Linux 64的具体信息。 – 2010-09-10 08:37:11

8

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

最重要的(更多信息,请参见下面的实施例):在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作为常用函数。要看到它,我们来考虑下面的例子。

与-nostdlib

没有C运行时/ GCC让我们来看看这个简单的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运行时

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

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

我们与(C运行时的缺省设置是使用)构建它:用

./exit64gcc first second third 

在gdb

gcc exit64gcc.s -o exit64gcc 

运行在该断点处停止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运行时和代码不涉及的命令行参数传递代码之间的差。

+1

您可以构建一个定义'_start'的程序[使用'gcc -nostdlib'](http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-系统-GNU工具链/ 36901649#36901649)。如果只定义'main',而不是'_start',则必须使用gcc,但是定义'_start'并不会阻止您使用gcc。 – 2016-07-01 23:10:17

+0

查看寄存器/内存是了解* *可能*如何工作的一种好方法,即找到某些东西去搜索官方文档。尽管如此,你不应该假设你找到的任何东西都保证在那里。您不希望依赖某些实际上只是剩余副本的内容,并且可能不在将来的版本中。你的结论是正确的,但是继续参考SysV ABI如何保证这些值会在那里会好得多。 – 2016-07-01 23:13:22

+0

你也没有提到提供'_start'并调用你的'main'的glibc CRT代码。定义'main'或'_start'没有什么神奇的区别;不难理解启动样板代码如何收集流程启动状态并用这些函数参数调用'main'。 – 2016-07-01 23:15:19