gcc 从C调用汇编函数

aor9mmx1  于 2022-11-13  发布在  其他
关注(0)|答案(5)|浏览(191)

我试图从c调用一个汇编函数,但我总是得到错误。

.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

这是我的汇编函数,文件名为integrate. s。

#include <stdio.h>

extern int integrate(int from,int to);

void main()
{
    printf("%d",integrate(1,10));
}

这是我的C代码。

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

每当我尝试用gcc -Wall function.c -o函数编译代码时,它都会给出“未定义对integrate的引用”错误。我还尝试从c中添加到integrate.s文件的链接,如

#include<(file path)/integrate.s>

但是它并没有很好地工作。顺便说一句,汇编代码在做什么并不重要,因为现在我只是试图从c成功地调用函数。有人能帮助我解决这个问题吗?

xv8emn3q

xv8emn3q1#

我发现代码存在以下问题:

  • 调用约定要求您必须保留edi的值
  • cmp %edi,1024正在使用1024作为地址,可能会出错。您需要cmp $1024,%edi与立即数进行比较
  • 每次迭代都从参数重新加载eaxecx,因此执行的计算没有任何效果
  • 您似乎没有将任何合理的返回值放入eax(它将返回传入的from的值)

即使“汇编代码在做什么并不重要”,前两点也适用。

jslywgbw

jslywgbw2#

不确定你是否已经解决了这个问题,但这里是我如何做到这一点。
编译时,请确保添加这两个文件:$gcc main.c print_msg.s -o main
要单独运行汇编程序文件,请执行以下操作:$as print_msg.s -o print_msg.o后跟$ld print_msg.o -e print -o print_msg。请注意,如果您只想从C文件运行它,则不需要这样做。
汇编程序文件: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

然后将C文件:main.c

extern void print(void);

int main(void)
{
    print();
    return 0;
}
c0vxltue

c0vxltue3#

x86-64 Linux示例

这里已经有了一个答案,它显示了如何调用void func(void),但这里有一个x86-64 Linux示例,它接受参数并有一个返回值,这就是问题中所问的。(这个问题和其他一些答案使用的是32位代码,它有不同的调用约定)。
首先,让我们简化装配函数:

# 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

这是使用System V呼叫惯例,其中函式传回值在rax中传回,而函式接收的参数则在rdirsirdxrcxr8r9中传递,然后以相反的顺序传递堆栈。(What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64)。例如:

long add_four_nums(int first, long second, short third, unsigned fourth);

使用此原型声明的函数将在%edi中接收first,在%rsi中接收second,在%dx中接收third,在%ecx中接收fourth。它将在%rax中返回它的result
现在我们已经编写了程序集(尽管该函数主要是一个存根,用于显示如何接受参数并返回值),您可以在C文件中使用该函数,就像您当前所拥有的那样:

#include <stdio.h>
extern int integrate(int from,int to);
int main() {
    printf("%d\n", integrate(1,10));
}

它可以被编译并与gcc链接,然后运行,如下所示:

$ gcc -o combined -Wall main.c integrate.s   && ./combined
vsdwdz23

vsdwdz234#

警告:'main'的传回型别不是'int'
表示'main'的传回型别不是'int'...将它变更为int,然后:

int main()
{
}

此外,要解决链接器错误,请调用GCC作为

gcc -o myprog main.c integrate.s

这应该可以。

8tntrjer

8tntrjer5#

第一次变更:

/* void main() */
int main(void)

main不能为void,必须返回一个整数(通过错误)。为了使C能够看到您的integrate函数,您必须在头文件或C文件中指定它的函数签名,然后您可以执行以下操作:

as -o integrate.o integrate.s

然后,执行:

gcc -Wall -c c_program.c

最后,执行:

gcc c_program.o integrate.o -o integrate

然后尝试:

./integrate

sudo chmod u+x ./integrate && ./integrate

注意:头文件应类似于:

/* integrate.h */
#ifndef INTEGRATE
#define INTEGRATE
return_type integrate(ARGS, ...); /*if any ARGS are passed*/
#endif

并将#include "integrate.h"放在C文件的顶部。

相关问题