从x86汇编语言调用C函数

32

是否可以使用GCC从C函数生成汇编语言函数,以便可以从汇编语言程序中调用它们?我知道gcc将C编译为机器码(可以轻松地反汇编为汇编语言),而且我已经知道可以在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

我不记得所有的细节,但你只需要编译成目标文件并将它们链接在一起。你只需要知道在调用函数时使用了哪些调用约定。 - Jeff Mercado
1
你可以添加汇编代码,展示你尝试了什么,并说明它无法编译或行为错误。这样回答问题时就更容易指出问题所在了。 - hyde
3
你可以学习如何做到这一点,通过编写一个调用目标函数的C函数,并将其编译成汇编语言(-S),然后研究结果。我还会指向"psABI",但我找不到副本了。 - zwol
2
当然可以做到。只需遵循编译器ABI的规则即可。 - David Heffernan
2个回答

39

我在这里凭记忆说话,所以某些细节可能有些不准确。然而,它应该足够让你朝着正确的方向前进。

你需要告诉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()只接收一个参数,而在我的例子中,我使用了四个参数。请根据需要调整我的例子。

最后,你需要编译你的C和汇编文件成目标文件,链接这些目标文件,然后执行。

希望这可以帮到你。


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

2

对于x86_64架构,需要注意一些额外的事项:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接