代码之家  ›  专栏  ›  技术社区  ›  Alex F

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

  •  6
  • Alex F  · 技术社区  · 14 年前

    此说明对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 回复  |  直到 7 年前
        1
  •  9
  •   rkhayrov    14 年前

    看起来像第3.4节 进程初始化 特别是图3.9 System V AMD64 ABI 准确描述你想知道的。

        2
  •  8
  •   ead    7 年前

    尽管公认的答案已经足够了,但我还是想给出一个明确的答案,因为还有一些其他的答案可能会让人困惑。

    最重要的(有关更多信息,请参阅下面的示例):在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 ,包装命令行参数和调用 主要的 作为一个共同的功能。要查看它,我们考虑下面的示例。

    没有带-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
    

    在断点处停止 启动 . 让我们检查一下寄存器:

    (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运行时

    我们必须稍微改变程序,重新命名 启动 进入之内 主要的 ,因为入口点 启动 由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
    

    在断点处停止 主要的 . 什么在堆栈中?

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

    看起来不熟悉。还有注册?

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

    我们可以看到 rdi 包含 氩弧焊 价值。但是如果我们现在检查指针 rsi 奇怪的事情发生了:

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

    但是等等,第二个论点 主要的 C中的函数不是 char * 但是 char ** 也:

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

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

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

        3
  •  1
  •   Dean Pucsek    14 年前

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