从c调用汇编函数

从c调用汇编函数,c,linux,assembly,gcc,x86,C,Linux,Assembly,Gcc,X86,我试图从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

我试图从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));
}
每当我尝试使用gcc-Wall function.c-o函数编译代码时,它都会给出“未定义的集成引用”错误

#include<(file path)/integrate.s>
#包括
顺便说一句,汇编代码在做什么并不重要,因为现在我只是想成功地从c调用函数。有人能帮我解决这个问题吗

警告:“main”的返回类型不是“int”

表示“main”的返回类型不是“int”。。。将其更改为
int
,然后:

int main()
{
}
另外,要解决链接器错误,请根据需要调用GCC

gcc -o myprog main.c integrate.s

这应该行得通。

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

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

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

不确定您是否解决了这个问题,但下面是我如何解决这个问题的

编译时,确保添加两个文件:
$gcc main.c print\u msg.s-o main

要单独运行汇编程序文件:
$as print\u msg.s-o print\u msg.o
,后跟
$ld print\u msg.o-e print-o print\u msg
。请注意,如果您只想从C文件运行它,则不需要这样做

汇编程序文件:
print\u 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
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
这是使用调用约定,其中函数返回值在
rax
中传回,函数接收的参数在
rdi
rsi
rdx
rcx
r8
r9
中传回,然后按相反顺序将堆栈传递。(). 例如:

long add_four_nums(int first, long second, short third, unsigned fourth);
使用此原型声明的函数将在
%edi
中接收
第一个
%rsi
中接收
第二个
%dx
中接收
第三个
,在
%ecx
中接收
第四个
。它将在
%rax
中返回其
结果

现在我们已经编写了程序集(虽然函数主要是一个存根,用于显示如何接受参数和返回值),您可以在C文件中使用该函数,就像您当前使用的那样:

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

它的工作原理类似于
gcc mprog main.c integrate.s-o out
。但这次它说的是分段
fault:core dumped
。这与汇编函数中的错误有关?@AliU。现在仍然是。(你为什么多次发布此评论?)仍然是?你的意思是这是汇编函数的错误?(错误地删除了它,对不起。)@AliU。如果它编译并链接,但随后出现SEGFULTS,那么是的,我怀疑这是汇编函数的错误。您是否在运行可执行文件时附加了调试器?Jester的回答至少应该使您的程序不会崩溃。你也许应该记下他的答案。我正在考虑发布完整的汇编代码,但是在不知道汇编程序应该做什么的情况下,我真的只能做他的回答。来自
的第一个参数
包含在%rdi-Nope中,在C中,您告诉编译器它是一个
int
,因此只有EDI保证保存您的值,不一定是零或符号扩展到64位RDI。实际上,传递常量的简单调用程序会将其零扩展到RDI中,但是注释仍然是错误的,除非您将类型更改为
uint64\t
long
。当然,ESI也是如此。@PeterCordes很酷,谢谢你的反馈,如果你认为有帮助的话,我可以更新答案,剩下的答案看起来还可以。除此之外,一切看起来都是正确的;回答得很好,很好地解释了必要的拼图块以及它们是如何组合在一起的。(除了ISO C规定的
main
的返回类型必须是
int
,而不是
void
,对于“托管”程序(即在操作系统下运行的程序,而不是独立的程序))是的,应该更新它。我决定自己做这件事,以便在评论和编辑中节省大量时间。@PeterCordes非常感谢您的时间和反馈!
long add_four_nums(int first, long second, short third, unsigned fourth);
#include <stdio.h>
extern int integrate(int from,int to);
int main() {
    printf("%d\n", integrate(1,10));
}
$ gcc -o combined -Wall main.c integrate.s   && ./combined