如何在x86体系结构上从C调用汇编函数? [英] How to call Assembly Functions from C on x86 architecture?

查看:57
本文介绍了如何在x86体系结构上从C调用汇编函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不幸的是,互联网上缺乏有关如何将x86汇编实现为C程序的指南.另外,无聊的人也想知道不同的语法(IT& T和Intel).

Unfortunately, the internet is lacking a guide on how to implement the x86 Assembly into C programs. Also, noobies are wondering about the different syntaxes (IT&T and Intel).

我个人不确定如何处理NASM或GCC等不同的编译器/汇编器.

Personally, I am not sure how to exactly do this, handling the different compilers/assemblers like NASM or GCC.

我创建了以下高度复杂的程序:

I have created the following, highly sophisticated program:

#include <stdio.h>

extern int add_imm(int,int);

int main(int argv, char** argc){
  int a = 2;
  int b = 3;
  int c = add_imm(a,b);
  printf("%d", c);
}

与其他高度复杂的程序一起:

Together with this other highly sophisticated program:

SECTION .TEXT
      GLOBAL add_imm

add_imm:
    push rbp
    mov rsp, rbp
    mov rax, [rbp+24]
    mov rbx, [rbp+16]
    add rax, rbx
    pop rbp
    ret

使用以下代码进行编译:

Compiling that with:

nasm -f elf64 add_imm.S
gcc main.c add_imm.o -o tes

引发编译器错误:

Undefined symbols for architecture x86_64:
  "_add_imm", referenced from:
      _main in main-019ea6.o
ld: symbol(s) not found for architecture x86_64

因此,我认为如果有人可以创建一个关于如何将汇编语言真正嵌入到C中的解释,那将是值得的.有什么重要的考虑因素?一般的通话约定是什么?

So I thought it would be worthwhile if someone could create an explanation of how to really embed assembly language into C. What is important to consider? What are the general calling conventions?

注意:我正在使用macOS,但我希望它也能在Linux上运行.

Note: I am using macOS, but I hope it will work on Linux as well.

推荐答案

在汇编中编写函数所需要做的关键是遵循您要为其编程的平台的ABI(应用程序二进制接口).该文档指定:

The key thing you need to do to write a function in assembly is to follow the ABI (application binary interface) of the platform you are programming for. This document specifies:

  • C语言中的类型( int long float ,...)和标准库的宽度是
  • 结构如何在内存中布置
  • 如何调用函数(函数调用顺序),特别是
    • 参数如何传递给函数
    • 哪些寄存器需要由函数保存
    • 如何将返回值返回给调用者
    • 如何实际调用函数
    • what the width of types in the C language (int, long, float, ...) and standard library are
    • how structures are laid out in memory
    • how functions are called (function call sequence), specifically
      • how arguments are passed to functions
      • what registers need to be preserved by functions
      • how the return value is returned to the caller
      • how to actually call the function

      每种操作系统和体系结构的ABI都不相同,因此您不应对此做任何假设.最重要的是,即使一个操作系统和体系结构具有相同的操作系统或体系结构,它们的代码示例也极有可能在另一操作系统和体系结构上不起作用.x86_64-linux与x86_64-osx是不同的平台!

      The ABI is different for each operating system and architecture and you should not make assumptions about it. Most importantly, code examples for one operating system and architecture will most likely not work on another, even if they have the same operating system or architecture. x86_64-linux is a different platform than x86_64-osx!

      对于x86_64-osx,相关的ABI文档为 OS XABI函数调用指南,其中引用了 x86_64 SysV ABI .主要区别在于标识符的修饰方式(带有下划线)和有关共享对象的一些详细信息,这些信息现在不必一定要引起您的关注.

      For x86_64-osx, the relevant ABI document is the OS X ABI Function Call Guide which refers to the x86_64 SysV ABI. The main difference is in how identifiers are decorated (with a leading underscore) and some details about shared objects which don't necessarily need to concern you right now.

      对于特别讨厌的细节,通常最好编写一些C代码,然后要求编译器使用 -S 选项生成汇编代码.要在汇编中执行相同的操作,只需执行编译器所做的任何事情即可.

      For particularly pesky details, it is usually a good idea to write some C code and then ask the compiler to generate assembly code with the -S option. To do the same thing in assembly, just do whatever the compiler does.

      已经解决了这个问题,这是您需要做的:

      Having gotten that out of the way, here is what you need to do:

      • 确定功能的名称;我们称之为 foo
      • 在要从中使用它的C源代码或头文件中使用适当的类型签名将函数声明为外部函数

      • decide on a name for your function; let's call it foo
      • declare the function as external with an appropriate type signature in the C source or header files you want to use it from

      extern int foo(int, int);
      

    • 参考ABI文档,找到与您的函数标识符相对应的符号;对于x86_64-osx,您需要在标识符前加上下划线以获取符号(即 _foo )

      使用您喜欢的汇编器在汇编文件中编写函数.确保生成正确类型的目标文件.对于x86_64-osx,正确的类型是 macho64 ,因此您应该这样组装:

      program your function in an assembly file using your favourite assembler. Make sure to generate an object file of the correct type. For x86_64-osx, the correct type is macho64, so you should assemble like this:

      nasm -f macho64 foo.asm
      

    • 确保遵守调用约定.请参阅本文进行简短介绍,或阅读上面链接的ABI文档.您应该真正阅读它.

    • make sure to obey the calling convention. Refer to this article for a short introduction or read the ABI document linked above. You should really read it.

      这是您做错了什么的摘要:

      Here's a summary of what you did wrong:

      • 您没有使用正确的符号名称,因为您忘记了用下划线装饰它
      • 您没有正确遵循通话约定
      • 您生成的对象文件类型不正确( elf64 而不是 macho64 )
      • 您对节命名不正确(请参阅nasm文档,以了解macho64接受的节名称)
      • 您犯了一些编程错误,其他人指出这些错误使您的函数无法正常工作

      这篇关于如何在x86体系结构上从C调用汇编函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆