calling assembly function from c
I'm trying to call an assembly function from c,but i keep getting errors.
.text
.globl integrate
.type integrate, @function
integrate:
push %ebp
mov %esp, %ebp
mov $0,%edi
start_loop:
cmp %edi,1024
je loop_exit
mov 8(%ebp),%eax
mov 12(%ebp),%ecx
sub %eax,%ecx
add %edi,%ecx
incl %edi
jmp start_loop
loop_exit:
movl %ebp, %esp
popl %ebp
ret
This is my assembly function,file called integrate.s.
#include <stdio.h>
extern int integrate(int from,int to);
void main()
{
printf("%d",integrate(1,10));
}
Heres my c code.
function.c:5:6: warning: return type of ‘main’ is not ‘int’ [-Wmain]
/tmp/cciR63og.o: In function `main':
function.c:(.text+0x19): undefined reference to `integrate'
collect2: ld returned 1 exit status
Whenever i try to compile my code with gcc -Wall function.c -o function,it gives the 'undefined reference to integrate' error.I also tried adding link to the integrate.s file from c,like
#include<(file path)/integrate.s>
but it didnt work as well.Btw what assembly code is doing is not important,for now im just trying to call the function from c successfully.Can anyone help me about solving this problem ?
Solution 1:
I see the following problems with the code:
- calling convention mandates you must preserve the value of
edi
-
cmp %edi,1024
is using1024
as address and will probably fault. You wantcmp $1024,%edi
for comparing with an immediate number - you are reloading
eax
andecx
from the arguments each iteration so the calculation you perform has no effect - you don't seem to put any sensible return value into
eax
(it will return the value offrom
that was passed in)
The first two points apply even if "what assembly code is doing is not important".
Solution 2:
Not sure if you have solved this or not, but here is how I have done this.
When compiling make sure to add both files: $gcc main.c print_msg.s -o main
To run the assembler file on its own: $as print_msg.s -o print_msg.o
followed by $ld print_msg.o -e print -o print_msg
. Note that this is not required if you only want to run it from your C file.
The assembler file:
print_msg.s
# A program to be called from a C program
# Declaring data that doesn't change
.section .data
string: .ascii "Hello from assembler\n"
length: .quad . - string
# The actual code
.section .text
.global print
.type print, @function #<-Important
print:
mov $0x1,%rax # Move 1(write) into rax
mov $0x1,%rdi # Move 1(fd stdOut) into rdi.
mov $string,%rsi # Move the _location_ of the string into rsi
mov length,%rdx # Move the _length_ of the string into rdx
syscall # Call the kernel
mov %rax,%rdi # Move the number of bytes written to rdi
mov $0x3c,%rax # Move 60(sys_exit) into rax
syscall # Call the kernel
then the C file: main.c
extern void print(void);
int main(void)
{
print();
return 0;
}
Solution 3:
warning: return type of ‘main’ is not ‘int’
means that the return type of ‘main’ is not ‘int’... Change it to int
, then:
int main()
{
}
Also, to solve the linker error, invoke GCC as
gcc -o myprog main.c integrate.s
and that should work.
Solution 4:
x86-64 Linux example
There is already an answer here which shows how to call a void func(void)
, but here is an x86-64 Linux example that accepts parameters and has a return value, which was what was asked in the question. (The question and some other answers are using 32-bit code, which has a different calling convention).
To start off, let's simplify the assembly function:
# Need to make it global so it can be accessed in another file with extern
.globl integrate
# Cannot hurt to define it as a function type, sometimes useful for dynamic linking, see comments in: https://stackoverflow.com/questions/65837016/how-to-call-a-function-in-an-external-assembly-file#comment116408928_65837016
.type integrate, @function
integrate:
# int integrate(int from /*EDI*/, int to /*ESI*/)
# INPUT:
# the first parameter `from` is contained in %edi, the int-sized low half of %rdi
# the second parameter `to` is contained in %esi
# OUTPUT:
# return is passed in %eax;
# you can leave garbage in the high half of RAX if convenient
lea 123(%rdi, %rsi), %ecx # from + to + 123 just for example
# (main work of function done)
mov %ecx, %eax # it seems your return value is in %ecx
# but we need it in %eax for the return value to C
# or just use EAX instead of ECX in the first place to avoid this instruction
ret
This is using the System V calling convention, where the function return value is passed back in rax
and the parameters that the function receives are passed in rdi
, rsi
, rdx
, rcx
, r8
, r9
, then the stack in reverse order. (What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64). For example:
long add_four_nums(int first, long second, short third, unsigned fourth);
The function declared with this prototype would receive first
in %edi
, second
in %rsi
, third
in %dx
, and fourth
in %ecx
. It would return its result
in %rax
.
Now that we have the assembly written (though the function is mainly a stub to show how to accept arguments and return a value), you can use that function in your C file like you currently have:
#include <stdio.h>
extern int integrate(int from,int to);
int main() {
printf("%d\n", integrate(1,10));
}
It can be compiled and linked with gcc, then run, as:
$ gcc -o combined -Wall main.c integrate.s && ./combined