从c调用汇编函数

Ali*_* U. 6 c x86 assembly inline-assembly

我试图从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   
Run Code Online (Sandbox Code Playgroud)

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

#include <stdio.h>

extern int integrate(int from,int to);

void main()
{
    printf("%d",integrate(1,10));
}
Run Code Online (Sandbox Code Playgroud)

继承人我的代码.

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
Run Code Online (Sandbox Code Playgroud)

每当我尝试使用gcc -Wall function.c -o函数编译我的代码时,它会给出'未定义的集成引用'错误.我也试过从c添加到integration.s文件的链接,就像

#include<(file path)/integrate.s>
Run Code Online (Sandbox Code Playgroud)

但它也没有用.比如汇编代码正在做什么并不重要,现在我只是试图从c成功调用函数.任何人都可以帮我解决这个问题吗?

sam*_*249 11

x86-64 Linux 示例

这里已经有一个答案展示了如何调用 a 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: /sf/ask/4608591151/#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
Run Code Online (Sandbox Code Playgroud)

这是使用System V调用约定,其中函数返回值被传回rax,函数接收的参数被传入rdi, rsi, rdx, rcx,r8 , r9,然后以相反的顺序传入堆栈。(i386 和 x86-64 上 UNIX 和 Linux 系统调用(和用户空间函数)的调用约定是什么)。例如:

long add_four_nums(int first, long second, short third, unsigned fourth);
Run Code Online (Sandbox Code Playgroud)

使用此原型声明的函数将接收firstin %edi, secondin %rsi, thirdin%dxfourthin %ecx。它将返回其resultin %rax

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

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

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

$ gcc -o combined -Wall main.c integrate.s   && ./combined
Run Code Online (Sandbox Code Playgroud)

  • *第一个参数“from”包含在 %rdi* 中 - 不,在 C 中,您告诉编译器它是一个“int”,因此只有 EDI 才能保证保存您的值,不一定是零或符号扩展为 64 位RDI。实际上,传递常量的简单调用者*会将其零扩展为 RDI,但注释仍然是错误的,除非您将类型更改为“uint64_t”或“long”。当然,ESI 也是如此。 (2认同)

Jes*_*ter 9

我看到代码存在以下问题:

  • 调用约定要求你必须保留值 edi
  • cmp %edi,1024正在使用1024地址,可能会出错.您想要cmp $1024,%edi与即时数字进行比较
  • 你重装eax,并ecx从参数每次迭代,所以你执行计算没有影响
  • 你似乎没有把任何合理的回报值eax(它将返回from传入的值)

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


小智 5

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

意味着'main'的返回类型不是'int'...将其更改为int,然后:

int main()
{
}
Run Code Online (Sandbox Code Playgroud)

另外,要解决链接器错误,请将GCC调用为

gcc -o myprog main.c integrate.s
Run Code Online (Sandbox Code Playgroud)

这应该工作.


Olo*_*ord 5

不确定你是否已经解决了这个问题,但这是我如何做到的。

编译时确保添加两个文件: $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
Run Code Online (Sandbox Code Playgroud)

然后是C文件: main.c

extern void print(void);

int main(void)
{
    print();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)