【问题标题】:Calling C functions from x86 assembly language从 x86 汇编语言调用 C 函数
【发布时间】:2013-04-21 18:13:29
【问题描述】:

是否可以使用 GCC 从 C 函数生成汇编语言函数,以便可以从汇编语言程序中调用它们?我知道gcc把C编译成机器码(可以很容易地反汇编成汇编语言),而且我已经知道inline assembly language functions in C是可以的,但是我还没有找到从汇编语言程序中调用C函数的方法,这基本上与此相反。

在这里,我试图在 x86 汇编程序中内联 C 函数。如果内联是不可能的,那么还有其他方法可以从汇编语言程序中调用 C 函数吗?

.686p
.model flat,stdcall
.stack 2048

.data

.code
start:

invoke  ExitProcess, 0

printSomething PROC ;now I'm attempting to inline a C function here
    void printSomething(thingToPrint){
        printf("This is a C function that I want to invoke from an assembly language program.");
        printf("There must be some way to do this - is it possible somehow?");
    }
printSomething ENDP

end start

【问题讨论】:

  • 我不记得所有细节,但您只需要编译为目标文件并将它们链接在一起。您只需要知道调用函数时使用了哪些调用约定。
  • 您可以添加汇编代码以显示您尝试过的内容,并说明它如何编译失败或它的行为如何错误。答案会更容易指出它的问题所在。
  • 您可以通过编写一个调用您要调用的函数的 C 函数、将其编译为汇编语言 (-S) 并研究结果来学习如何做到这一点。我还想把你指向“psABI”,但我再也找不到副本了。
  • 当然可以。只需遵循编译器 ABI 的规则即可。

标签: c assembly x86


【解决方案1】:

我在这里是凭记忆说的,所以我可能会稍微偏离一两个细节。但是,我希望这足以让您朝着正确的方向前进。

你需要告诉 GCC 汇编器你的例程 printSomething() 没有在你的汇编文件中定义。在“C”中,您将使用 extern 关键字。对于组装,您需要使用 .globl

.globl printSomething

如果您使用的汇编程序与 GCC 不同,则关键字可能不同。

下一个大问题是“我如何传递参数”?这在很大程度上取决于您的处理器和操作系统。由于您的问题标题指示 x86,我将假设您使用的是 16 位或 32 位模式和标准 x86 ABI(与 Windows 和 Linux 之间也不同的 x86-64 不同)。 C 参数通过将它们压入堆栈来传递给被调用的例程。它们从右到左被压入堆栈。

因此,

printSomething (arg1, arg2, arg3, arg4);

翻译成...

pushl arg4
pushl arg3
pushl arg2
pushl arg1
call  printSomething
addl  $0x10, %esp

你可能会问自己,这是什么

addl $0x10, %esp

?我们将四个 32 位参数传递(也称为推送)到例程(到堆栈上)。尽管例程知道期望这些参数,但它不负责将它们从堆栈中弹出。调用者对此负责。因此,从例程返回后,我们调整堆栈指针以丢弃我们之前压入堆栈的四个 32 位参数。

在上面的示例中,我假设我们在 32 位模式下运行。如果是 16 位模式,那就是……

pushw arg4
pushw arg3
pushw arg2
pushw arg1
call  printSomething
addw  $0x8, %sp

我意识到,在您的示例中,printSomething() 仅采用一 (1) 个参数,而在我的示例中,我使用了四 (4) 个参数。只需根据需要调整我的示例即可。

对于最后的步骤,您需要将 C 和汇编文件编译成目标文件,链接目标文件,然后执行。

我希望这会有所帮助。

【讨论】:

  • 现在我想知道是否可以使用 GCC 从 C 函数实际生成 x86 汇编程序。是否可以使用 GCC 或其他 C 编译器将 C 程序中的每个函数转换为等效的汇编语言函数?
  • @AndersonGreen - 您可以随时尝试使用 -S 选项。 “gcc -S file.c”将“file.c”转换成程序集(命名为file.s)。或者,如果您已经有了可执行文件,您可以使用“objdump -d exe_file > exe_file.asm”来反汇编“exe_file”并将结果转储到“exe_file.asm”中。
  • @Sparky 在 x86 过程约定中,每个函数在返回给调用者之前都有 leave 或等效指令。我认为您示例中的addl $0x10, %esp 是多余的。
  • @StrikeW - 示例中的“addl $0x10, %esp”用于清理压入堆栈的参数。在 C 中,调用者通常负责清理堆栈上的参数。被调用 C 函数中的“离开”指令(或等效指令)用于清理被调用函数使用的任何堆栈空间。如果,被调用函数负责清理堆栈上传递给它的参数,它将使用“RET n”指令(其中 n 是从堆栈中弹出的字节数)。
  • C 不编译以_ 开头的外部方法吗?所以电话应该是call _printSomething ... ???
【解决方案2】:

对于 x86_64,请注意您必须小心一些额外的东西:

【讨论】:

    猜你喜欢
    • 2019-02-13
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    相关资源
    最近更新 更多