从C++调用程序集代码

Calling Assembly code from C++

本文关键字:代码 程序集 调用 C++      更新时间:2023-10-16

我正在尝试编写自己的I/O库,所以第一步就是能够输出一个字符。到目前为止,这只需要在Linux上工作。我用x86(使用英特尔语法)编写了我认为是可靠的_putc:标签,并将其放在"io.h"中,然后从我的主文件"test.cpp"中调用它。以下是测试的内容.cpp:

#include "io.h"
extern void _putc(char) __asm__("_putc");
int main(int argc, char** argv){
    _putc('c');
    return 0;
}

这是我的IO.H:

asm(
"n.intel_syntax noprefix"
"n.globl _putc"
"n_putc:"
"ntpush [ebp]"
"ntmov ebp,esp"
"ntmov eax,4"
"ntmov ebx,0"
"ntmov ecx,[ebp+8]"
"ntmov edx,1"
"ntint 0x80"
"ntret"
"n.att_syntax" ;return to g++'s at&t syntax
);

我正在使用 G++ 在 x86_64 Linux 操作系统上开发这个

我正在使用它来编译:

 g++ -o test test.cpp -Wall

运行时,它会立即出现段错误。我认为我的问题是调用惯例,但没有办法绝对确定。我尝试将程序集包装在一个函数(void _putc(char c))中并删除全局标签,但这没有帮助。我还尝试推送ebp的内容而不是它指向的内容,但随后它无法编译。

注意:这只能与主文件中的行#include io.h一起使用(我可以在此操作后移动extern void _putc(char) __asm__("_putc");),并且它必须与C++一起使用,而不仅仅是 C。另外,请不要在头文件中教我函数定义,这就是我正在做的事情。

对于 64 位代码,您确实需要考虑使用 syscall 而不是 int 0x80 。系统调用使用 64 位约定,系统调用号码与 32 位代码中的int 0x80不同。您可以了解此表中的 64 位系统调用。sys_writesys_read调用如下所示:

%rax  System call %rdi              %rsi             %rdx         %r10      %r8   %r9
0     sys_read    unsigned int fd   char *buf        size_t count         
1     sys_write   unsigned int fd   const char *buf  size_t count

如果您想了解如何在 64 位 Linux 代码中传递参数,则需要熟悉 System V 64-Linux ABI。在输入调用后,如果您打算调用其他函数或系统调用,通常必须将堆栈与 16 字节边界对齐。

您的代码还存在一个缺陷,即将字符传递给sys_writesys_write需要一个包含字符的缓冲区的地址,而不是字符本身。

这是一个可以工作的代码转储:

asm(
"n.intel_syntax noprefix"
"n.globl _putc"
"n_putc:"
"ntadd rsp, -8"   // Allocate space on the stack to store the character passed
                    // Also aligns stack back to 16-bye boundary 
                    // with return address already on stack
"ntmov [rsp],rdi" // Move the character passed in RDI to memory(stack) for sys_write
"ntmov eax,1"     // With 64-bit syscalls RAX = 1 for sys_write
"ntxor edi,edi"   // 1st parameter to syscall is in RDX. FD = 0
"ntlea rsi,[rsp]" // We need to pass pointer to memory containing our char
"ntmov edx,1"     // RDX = 1 character to print. Could reuse EAX instead of 1 for src
"ntsyscall"       // Make syscall (using syscall instead of int 0x80 for 64-bit)
"ntadd rsp, 8"    // Restore stack to state right after function entry
"ntret"
"n.att_syntax prefix" //return to g++'s at&t syntax
);