【问题标题】:Linux 64 command line parameters in Assembly汇编中的 Linux 64 命令行参数
【发布时间】:2011-04-10 15:14:02
【问题描述】:

此描述适用于 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 # 跳过 argv[0] popq %rsi # 获取 argv[1] 称呼 ... ... }

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

【问题讨论】:

    标签: linux command-line assembly 64-bit


    【解决方案1】:

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

    最重要的(有关更多信息,请参见下面的示例):在 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 运行时

    我们必须稍微改变程序,将_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 运行时的代码和不使用 C 运行时的代码之间传递命令行参数是不同的。

    【讨论】:

    • 你可以构建一个定义_startusing gcc -nostdlib的程序。如果你只定义main,而不是_start,你必须使用gcc,但是定义_start并不会阻止你使用gcc。
    • 查看寄存器/内存是了解某些可能如何工作的好方法,即获取一些东西来搜索以查找官方文档。但是,您不应该只是假设您找到的任何东西都保证在那里。您不想依赖实际上只是剩余的草稿副本,并且可能不会出现在未来版本中的东西。您的结论是正确的,但最好继续参考 SysV ABI 如何保证这些值将存在。
    • 您也没有提及提供_start 并调用您的main 的glibc CRT 代码。定义main 或_start 之间没有神奇的区别;不难理解启动样板代码如何收集进程启动状态并使用这些函数 args 调用 main。
    • @Peter Cordes 非常感谢您的评论,并尝试在我的编辑中考虑它们。任何进一步的 cmets 都非常感谢!
    • 这些变化正是我的想法。它仍然更像是一个 gdb 教程,而不仅仅是一个答案,但如果这就是你想要写的并且正在寻找放置它的地方,那很酷。在深入 gdb 以举例说明之前,您预先包含了 new-execed 进程的堆栈布局,这是必不可少的。它使答案可以略读,因此不需要解释的人可以找到好的部分。确实想要解释的人会知道它的方向,这样更容易理解。
    【解决方案2】:

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

    【讨论】:

    • 仅链接的答案很脆弱。那个页面对我不起作用。 www.x86-64.org 没有发送任何数据。 ERR_EMPTY_RESPONSE
    【解决方案3】:

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

    【讨论】:

    • 这描述了程序内部的调用约定。进程命令行参数可能有自己的规则。我需要 Linux 64 的这些特定信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 2011-03-03
    相关资源
    最近更新 更多