x64位汇编

时间:2011-02-16 22:09:15

标签: linux assembly integer 64-bit nasm

我不久前开始编程(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

提前致谢!

2 个答案:

答案 0 :(得分:4)

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

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

编辑抛开:现在有两个,只有两个理由在议会中写任何东西:

  1. 你正在编写不能单独用C语写的极少数深刻魔法之一(一个很好的例子就是libffi的内容)
  2. 您正在手动优化已经测量的内部循环子程序,使其对性能至关重要,并且C编译器的工作做得不够好。
  3. 否则只要写下C中的任何内容。你的继任者会感谢你。

    编辑:已检查系统电话号码。

答案 1 :(得分:2)

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

编辑:现在我看到有一个调用你的函数的C main()例程,我的答案完全关于你遇到的问题。