x86-64 linux程序集。在argv上使用write不会因为EFAULT而工作? "地址错误"

时间:2014-06-14 05:05:31

标签: linux assembly x86-64

我最近决定让x86-64程序集一试。我在显示argv

时遇到了问题

是的,我写的代码很糟糕,它做出了假设,并且没有检查错误,我知道,但我真的不认为这是造成这种情况的原因问题

这是我的计划归结为其核心。

        .globl  _start
        .text
main:
_start:
        movl    $10,%edx          # No. of chars to write
        movq    16(%rsp),%rcx     # argv[1]
        movl    $1,%ebx           # stdout
        movl    $4,%eax           # write
        int     $0x80

        movl    $0,%ebx
        movl    $1,%eax
        int     $0x80

我运行我的程序./myprog helloworldddddddddd

所以argv [0] =。/ myprog 和argv [1] = helloworlddddddddddd或我输入的任何内容

我的程序应该将argv [1]中的前十个字符写入stdout。

除非它不起作用。写入返回-14,这是错误EFAULT?这意味着不好的地址。

所以我写了这个

        .globl  _start
        .text
main:
_start:
        movq    16(%rsp),%rax
        movq    $bob,%rbx
        subq    %rcx,%rcx
.Lloop:
        movb    (%rax,%rcx),%dl
        movb    %dl,(%rbx,%rcx)
        cmpb    $0,%dl
        je      .Ldone
        addq    $1,%rcx
        jmp     .Lloop

.Ldone: movl    $10,%edx
        movq    $bob,%rcx
        movl    $1,%ebx
        movl    $4,%eax
        int     $0x80

        movl    $0,%ebx
        movl    $1,%eax
        int     $0x80

        .comm   bob,50

它将argv [1]复制到我称为bob的内存区域,然后尝试将此副本写入stdout。我确定它是非常糟糕的x86,但它确实有效。如果我编译并运行程序,它会输出argv [1]

中的所有内容

最后我在C

中写了这个
#include <unistd.h>

int main(int argc, char **argv) {
        write(1,argv[1],10);

        return 0;
}

这更像是我的第一个程序,而不是我的第二个程序,但也有效。在这个阶段,我完全感到困惑。

所以在我看来,&#34;写&#34;系统调用不允许读取我的程序的argv数组,但它可以读取副本。哦,如果我用C语言写它,它就可以了。这看起来很奇怪。谁能告诉我发生了什么,为什么?


编辑:

有人指出我使用混合的32位和64位代码。所以我改为100%64位。

第一个程序:

    .globl  _start
    .text
main:
_start:
    movl    $10,%edx                # No. of chars to write
    movl    16(%rsp),%esi           # argv[1]
    movl    $1,%edi                 # stdout
    movl    $1,%eax                 # write
    syscall

    movl    $0,%edi
    movl    $60,%eax
    syscall

第二个程序:

    .globl  _start
    .text
main:
_start:
    movq    16(%rsp),%rax
    movq    $bob,%rbx
    subq    %rcx,%rcx
.Lloop:
    movb    (%rax,%rcx),%dl
    movb    %dl,(%rbx,%rcx)
    cmpb    $0,%dl
    je      .Ldone
    addq    $1,%rcx
    jmp     .Lloop

.Ldone: movl    $10,%edx                # No. of chars to write
    movq    $bob,%rsi               # buffer
    movl    $1,%edi                 # stdout
    movl    $1,%eax                 # write
    syscall

    movl    $0,%edi                 # return 0
    movl    $60,%eax                # exit
    syscall

    .comm   bob,50

仍然是同样的错误,第一个程序仍然无法工作,即使它现在是64位,第二个程序仍然有效,即使它现在也是64位

第一个程序的64位版本的Strace。 (那个不起作用的人):

execve("./Myprog", ["./Myprog", "bananablahblah"], [/* 46 vars */]) = 0
write(1, 0x1491f543, 10)                = -1 EFAULT (Bad address)
_exit(0)                                = ?
+++ exited with 0 +++

1 个答案:

答案 0 :(得分:2)

你在这里犯了三个主要错误:

  1. 您将main_start混淆。它们不是同义词 - _start的标准C库实现在调用main之前进行了一些重要的初始化。您可能不想尝试重新实现_start - 不要在您的可执行文件中定义它;链接libc以获取它。

  2. 您正在尝试在64位可执行文件中使用32位系统调用(int $0x80)。这不能正常工作;特别是,它无法读取存在于4GB边界之上的内存,包括在你的堆栈上!您必须使用syscall指令进行64位系统调用。但请记住,它使用稍微不同的调用约定,并使用不同的电话号码!

  3. 您在需要64位等效位置的某些位置使用32位指令和寄存器。这有时适用于低指针和值,但会截断其他一些值。除非你特别需要更小的东西(例如,char的8位寄存器),否则习惯总是使用64位指令和64位代码寄存器。