将值从 C 程序传递到汇编语言

Pass values from C program to Assembly language

我想使用链接汇编方法而不是 C 中的内联汇编方法将值从 C 程序传递到程序集。 下面是正在开发的汇编程序(GCD)。

;gcdasm.nasm
bits 64
section .text
global gcdasm
gcdasm:
    push rbp
    mov rbp, rsp
    mov rax, [rbp+4]        ;load rax with x
    mov rbx, [rbp+8]        ;load rbx with y
top:
    cmp rax, rbx            ;x(rax) has to be larger than y(rbx)
    je exit                 ;if x=y then exit and return value y
    jb xchange              ;if x<y then swap x and y
modulo:
    cqo                     ;RDX:RAX sign extend
    div rbx                 ;div rdx:rax with rbx
    cmp rdx, 0              ;check remider if its 0
    je exit                 ;if reminder is 0 then exit return return y
    mov rax, rdx            ;reminder rdx as next dividend
    jmp modulo              ;loop 
xchange:
    xchg rax, rbx           ;swap x and y
    jmp modulo

exit:
    mov rax, rbx            ;Return c program with the divisor y
    mov rsp, rbp
    pop rbp
    ret

这是我试图将值传递给汇编程序的 C 程序

//gcd.c
#include<stdio.h>

extern int gcdasm(int x, int y); 

int main(void){
    int x=0;
    int y=0;
    int result=0;

    x = 46; 
    y = 90; 
    printf("%d and %d have a gcd of %d\n", x,y,gcdasm(x,y));

    x = 55;
    y = 66;
    printf("%d and %d have a gcd of %d\n", x,y,gcdasm(x,y));

    return 0;
}

当我使用下面的方法编译时 运行 它。我收到错误 Floating point exception 或等待输入的空提示

$ nasm -felf64 gcdasm.nasm -o gcdasm.o
$ gcc gcdasm.o gcd.c -o gcd
$ ./gcd 
Floating point exception
$ ./gcd 

我无法找出错误。请帮帮我。 谢谢你。

正在将参数传递给 gcdasm()

两个int参数是通过寄存器而不是堆栈传递的。第一个和第二个参数分别在 rdirsi 的 lower-half 中传递(即:ediesi)。因此,通过将 ediesi 符号分别扩展到 raxrbx,您将传递的参数加载到这些寄存器中:

movsx rax, edi  ;load rax with x
movsx rbx, esi  ;load rbx with y

但是,注意rbx不是scratch register,因此callee需要在修改前保存它然后在离开 gcdasm 函数之前恢复它。

您可以简单地将代码中的任何地方的 rbx 替换为 rcx(这不是 callee-saved 寄存器)。你根本不需要rbp,所以你可以删除所有出现rbp的指令。


其他问题

  • 程序逻辑也有问题:

    mov rax, rdx   ;reminder rdx as next dividend
    

    除此之外,除数 (rcx) 应该变成 除数 (rax) 并且余数 (rdx) 应成为 除数 (rcx),即:

    mov rax, rcx
    mov rcx, rdx
    
  • signed值时,必须使用idiv指令,而不是div.


改进

对于 .

使用 test rdx, rdx 而不是 cmp rdx, 0 也有一些关于性能和代码大小的原因

考虑到以上所有内容:

;gcdasm.nasm
bits 64
section .text
global gcdasm
gcdasm:
    movsx rax, edi          ;load rax with x
    movsx rcx, esi          ;load rcx with y
top:
    cmp rax, rcx            ;x(rax) has to be larger than y(rcx)
    je exit                 ;if x=y then exit and return value y
    jb xchange              ;if x<y then swap x and y
modulo:
    cqo                     ;sign extend RDX:RAX
    idiv rcx                ;rdx:rax/rcx (signed values)
    test rdx, rdx           ;check whether remainder is zero
    je exit                 ;if reminder is 0 then exit return y
    mov rax, rcx            ;divisor becomes dividend
    mov rcx, rdx            ;remainder becomes divisor
    jmp modulo              ;loop 
xchange:
    xchg rax, rcx           ;swap x and y
    jmp modulo

exit:
    mov rax, rcx            ;Return c program with the divisor y
    ret