程序集中的Linux 64命令行参数

时间:2010-09-10 08:24:19

标签: linux command-line assembly 64-bit

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

我需要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 ...
...
}

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

3 个答案:

答案 0 :(得分:9)

看起来第3.4节流程初始化,特别是图3.9,在已经提到的System V AMD64 ABI中准确描述了您想知道的内容。

答案 1 :(得分:8)

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

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

public interface IHasPredicateGetter<T> {
   [NotNull] Expression<Func<T, bool>> GetPredicateFromString([NotNull] string pValue);
}

public class Movie : IHasPredicateGetter<Movie> {
   public int ID { get; set; }
   public string Name { get; set; }

   public Expression<Func<Movie, bool>> GetPredicateFromString(string pValue) {
      return m => m.Name.Contains(pValue);
   }
}

它与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 ... %rdi等等。

还有一件事:不应该推断出C %rsi - 函数的逆向工程行为。 C运行时提供入口点main,包装命令行参数并调用_start作为常用函数。为了看到它,让我们考虑以下示例。

没有C运行时/ GCC与-nostdlib

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

main

我们用以下方式构建它:

.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

使用

在gdb中运行
gcc -nostdlib exit64.s -o exit64

并停在./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。接下来的4个值看起来很像指针。让我们看看第二个指针:

argc

正如预期的那样,它是第一个命令行参数。

因此有实验证据表明命令行参数是通过x86-64中的堆栈传递的。但是,只有通过阅读ABI(如建议的接受答案),我们才能确定,情况确实如此。

使用C运行时

我们必须稍微更改程序,将(gdb) print (char[5])*(140737488347711) $1 = "first" 重命名为_start,因为入口点main由C运行时提供。

_start

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

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

使用

在gdb中运行
gcc exit64gcc.s -o exit64gcc

并停在./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

但等等,C中(gdb) print (char[5])*($rsi) $1 = "\211\307???" 函数的第二个参数不是main,而char *也是:

char **

现在我们找到了我们的参数,这些参数通过寄存器传递,就像x86-64中的普通函数一样。

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

答案 2 :(得分:1)

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