【问题标题】:Can't understand nasm error. How to fix code.无法理解 nasm 错误。如何修复代码。
【发布时间】:2018-09-01 20:07:36
【问题描述】:

我尝试从 asm 代码调用 printf 函数。

你好.asm:

%macro exit 0
    mov eax, 1
    mov ebx, 0
    int 80h
%endmacro

extern   printf      ; the C function, to be called

SECTION .data
    hello:     db   'Hello world!', 0

SECTION .text
    GLOBAL main

main:
    sub 8, rsp
    push dword hello
    call printf      ; Call C function
    add 8, rsp
    exit

生成文件:

all:
    nasm -f elf64 hello.asm -o hello.o
    ld hello.o -e main -o hello -lc -I/lib/ld-linux.so.2

clean:
    rm -f hello.o hello

打电话:

nasm -f elf64 hello.asm -o hello.o
hello.asm:16: error: invalid combination of opcode and operands
hello.asm:19: error: invalid combination of opcode and operands
make: *** [all] Error 1

请解释错误以及如何修复代码。

谢谢。

【问题讨论】:

  • subadd 的操作数向后。 Intel 语法是add dst, src,所以你使用sub rsp, 8。您是否从 AT&T 语法翻译并错过了它?
  • 另外,如果您使用 64 位代码(如 RSP 所示),则不应使用 int 80,而应使用 syscall,它也使用不同的寄存器。
  • @PeterCordes 是的。我修复了它,现在我收到错误 bash: ./hello: Accessing a corrupted shared library
  • @DavidWohlferd:是的,但是对于不需要 64 位输入(如指针)的系统调用(What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?),它仍然可以(在大多数系统上)工作。对于这种情况,更重要的是,x86-64 System V 调用约定不会在堆栈上传递参数。它应该是mov edi, hellolea rdi, [rel hello]What are the calling conventions for UNIX & Linux system calls on i386 and x86-64.
  • @e42d3: 与 gcc 链接,它知道如何传递正确的参数。 gcc -v -nostartfiles -no-pie hello.o。 (-v 打印它使用的实际链接器命令,以防你好奇)。不要使用-e main,调用你的入口点_start。 (并且不要与 RSP 混淆,它在 ELF 入口点已经是 16 字节对齐的,不像 main。或者更好的是,省略 -nostartfiles 这样正常的启动内容在 main 之前运行,而不是依赖于动态链接器在调用 stdio 函数之前初始化 libc 的东西。)

标签: assembly x86 nasm x86-64


【解决方案1】:

两条错误消息都提供了很好的线索。它们发生在第 16 行和第 19 行。

在第 16 行你有:

sub 8, rsp

这里的问题是你不能从字面常量中减去(任何东西)。我认为真正的意图是

sub rsp, 8

与第 19 行类似。而不是

add 8, rsp

你想要的是

add rsp, 8

请注意,对于subadd 等指令,第一个操作数获取运算结果。而文字常量不能做到这一点!

【讨论】:

  • 另外为了更好的理解,我觉得这里最好补充一下,NASM通常以op dest, src的方式接受指令,而op是操作,dest是目标,src是源.所以add esp, 8 等于 C 语法中的esp += 8
【解决方案2】:

工作解决方案:

你好.c:

extern exit      ; the C function, to be called
extern puts      ; the C function, to be called

SECTION .data
    hello:     db   'Hello world!', 0

SECTION .text
    GLOBAL _start

_start:
    mov edi, hello
    call puts      ; Call C function
    mov edi, 0
    call exit      ; Call C function

生成文件:

all:
    nasm -f elf64 hello.asm -o hello.o
    gcc -nostartfiles -no-pie hello.o -o hello

clean:
    rm -f hello.o hello

【讨论】:

  • 您的 push/pop 未对齐堆栈。它在进入_start 时已经是 16 字节对齐的,这与进入函数时不同。 (_start 不是一个函数,这就是为什么你不能使用它ret。)此外,你仍在使用 32 位 ABI 来处理 sys_exit。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?.
  • @PeterCordes:谢谢。
  • 您仍然没有修复对齐错误。如果您这样称呼puts,则允许它崩溃。您正在使用的版本恰好没有使用任何movaps 指令将内容复制到堆栈中/从堆栈中复制。如果你解决了这个问题,我会投票。不过,使用 libc exit(3) 代替系统调用是一件好事。它使您的程序在退出之前刷新 stdio 缓冲区,因此如果您将输出重定向到文件,它仍然可以工作。所以这比仅仅切换到mov eax,231 / syscall 要好。
猜你喜欢
  • 1970-01-01
  • 2013-03-12
  • 2019-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
相关资源
最近更新 更多