Transfer values ​​from C program to assembly language

I would like to pass values ​​from a C program to Assembly using the linked assembly method, rather than the built-in assembly method in C. Below is the Assembly program (GCD) I'm working on.

;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 

And this is program C from the application "I'm trying to pass values ​​to the build program

 //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; } 

When I compile using the method below and run it. I get either a Floating point exception error or an empty prompt awaiting input

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

I can not understand the error. Please help me. Thanks.

+5
source share
1 answer

Passing gcdasm() Arguments

Two int arguments are passed through registers, not onto the stack. The first and second arguments are passed in the lower half of rdi and rsi (i.e. edi and esi ) respectively. So, by the sign extending edi and esi to rax and rbx respectively, you load the passed arguments into these registers:

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

However, note that rbx not a zero register, so the called user needs to save it before modifying it, and then restore it before leaving the gcdasm function.

You can simply replace rbx with rcx (which is not a user-registered register) anywhere in your code. You don't need rbp at all, so you can delete all instructions where rbp appears.


Other problems

  • There is also a problem with the program logic:

     mov rax, rdx ;reminder rdx as next dividend 

    Instead, the divisor ( rcx ) should become a dividend ( rax ), and the remainder ( rdx ) should become a divisor ( rcx ), that is:

     mov rax, rcx mov rcx, rdx 
  • When dividing signed values, you should use the idiv , not the div .


Improvement

There are also some reasons regarding performance and code size for using test rdx, rdx instead of cmp rdx, 0 for comparing rdx with zero .


Given all of the above:

 ;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 
+4
source

Source: https://habr.com/ru/post/1275477/


All Articles