【问题标题】:x64 bit assemblyx64 位程序集
【发布时间】:2023-03-21 08:56:01
【问题描述】:

不久前我开始了汇编 (nasm) 编程。现在我制作了一个带有汇编实现的 C 函数,它打印一个整数。我使用扩展寄存器让它工作,但是当我想用 x64 寄存器(rax、rbx、..)编写它时,我的实现失败了。你们有没有看到我错过了什么?

main.c:

#include <stdio.h>

extern void printnum(int i);

int main(void)
{
        printnum(8);
        printnum(256);

        return 0;
}

32 位版本:

; main.c: http://pastebin.com/f6wEvwTq
; nasm -f elf32 -o printnum.o printnum.asm
; gcc -o printnum printnum.o main.c -m32

section .data
    _nl db 0x0A
    nlLen equ $ - _nl

section .text
    global printnum


printnum:
        enter 0,0

        mov eax, [ebp+8]

        xor ebx, ebx
        xor ecx, ecx
        xor edx, edx
        push ebx
        mov ebx, 10

startLoop:

        idiv ebx
        add edx, 0x30

        push dx ; With an odd number of digits this will screw up the stack, but that's ok
                ; because we'll reset the stack at the end of this function anyway.
                ; Needs fixing though.
        inc ecx
        xor edx, edx

        cmp eax, 0
        jne startLoop

        push ecx
        imul ecx, 2

        mov edx, ecx

        mov eax, 4 ; Prints the string (from stack) to screen
        mov ebx, 1
        mov ecx, esp
        add ecx, 4
        int 80h

        mov eax, 4 ; Prints a new line
        mov ebx, 1
        mov ecx, _nl
        mov edx, nlLen
        int 80h

        pop eax ; returns the ammount of used characters

        leave
        ret

x64 版本:

; main.c : http://pastebin.com/f6wEvwTq
; nasm -f elf64 -o object/printnum.o printnum.asm
; gcc -o bin/printnum object/printnum.o main.c -m64

section .data
    _nl db 0x0A
    nlLen equ $ - _nl

section .text
    global printnum

printnum:
    enter 0, 0

    mov rax, [rbp + 8]  ; Get the function args from the stac
    xor rbx, rbx
    xor rcx, rcx
    xor rdx, rdx

    push rbx        ; The 0 byte of the string
    mov rbx, 10     ; Dividor

startLoop:
    idiv rbx        ; modulo is in rdx
    add rdx, 0x30

    push dx

    inc rcx         ; increase the loop variable
    xor rdx, rdx        ; resetting the modulo

    cmp rax, 0
    jne startLoop

    push rcx        ; push the counter on the stack
    imul rcx, 2

    mov rdx, rcx        ; string length

    mov rax, 4
    mov rbx, 1
    mov rcx, rsp        ; the string
    add rcx, 4
    int 0x80

    mov rax, 4
    mov rbx, 1
    mov rcx, _nl
    mov rdx, nlLen
    int 0x80

    pop rax
    leave

    ret         ; return to the C routine

提前致谢!

【问题讨论】:

  • 请将您的代码放在这里,而不是把我们发送到其他网站。

标签: linux assembly integer 64-bit nasm


【解决方案1】:

我不确定这个答案是否与您看到的问题有关(因为您没有指定失败的原因),但 64 位代码的调用约定与 32 位代码不同做。两个主要的 64 位 Intel ABI(Windows 和 Linux/BSD/Mac OS)都在寄存器中而不是堆栈中传递函数参数。您的程序似乎仍在等待它们在堆栈中,这不是正常的处理方式。

编辑:现在我看到有一个调用您的函数的 C main() 例程,我的回答是正是关于您遇到的问题。

【讨论】:

  • 我明白了。什么时候我会在调用约定上搜索。似乎 idiv 指令失败。我得到类似浮点错误的东西
  • @Michel,我不确定您是否可以在 64 位模式下除以 rdx。指令集参考似乎表明,如果不是完全不允许的话,这将是一件奇怪的事情。
  • @Michel - 我刚刚写了一个测试程序 - 你不能使用 rdx 作为你的除数。也许这是你程序中的错字?您在 32 位代码中使用 ebx
  • 是的,我也看到了那个错字……现在已修复。现在它只打印两个空行。
【解决方案2】:

我认为您的问题是您尝试在 64 位模式下使用 32 位调用约定。如果你从 C 中调用这些汇编例程,那不会飞。64 位调用约定在此处记录:http://www.x86-64.org/documentation/abi.pdf

另外,不要打开代码系统调用。调用 C 库中的包装器。这样errno 得到正确设置,您可以利用sysenter/syscall,您不必处理正常调用约定和系统调用参数约定之间的差异,并且您可以与某些低级 ABI 问题。 (您的另一个问题是,write 是 Linux/x86-64 的系统调用编号 1,而不是 4。)

除了社论之外:现在有两个,而且只有两个理由在汇编中写任何东西:

  1. 您正在编写仅用 C 语言无法编写的极少数深度魔法之一(一个很好的例子是 libffi 的胆量)
  2. 您正在手动优化一个内循环子例程,该子例程测量对性能至关重要,而 C 编译器在这方面做得不够好。

否则就用 C 写任何东西。你的继任者会感谢你的。

编辑:检查系统调用号。

【讨论】:

  • 为什么这个答案被否决了?这是我 +1 的补偿。
  • 可能有人反对社论。 耸耸肩我的鼻子上没有皮肤。
  • 我知道我可以更好地使用 C 来解决这个问题,但这是出于纯粹的学习目的。 write 系统调用也在 4 的 x64 abi 中。现在我修正了一个错字,它只打印了两个空行(就像它应该的那样,只有 ints 丢失了)。我会读一些关于调用约定的东西,我应该能够修复。谢谢。
  • 不,真的,我刚刚检查过; __NR_writeasm/unistd_32.h 中是 4,​​但在 asm/unistd_64.h 中是 1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
  • 1970-01-01
相关资源
最近更新 更多