从 C++ 调用汇编代码
Calling Assembly code from C++
我正在尝试编写自己的 I/O 库,所以第一步只是能够输出一个字符。到目前为止,这只需要在 Linux 上起作用。我在 x86(使用 Intel 语法)中编写了我认为是可靠的 _putc:
标签,并将其放入 "io.h",然后从我的主文件 "test.cpp" 中调用它。这是test.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:"
"\n\tpush [ebp]"
"\n\tmov ebp,esp"
"\n\tmov eax,4"
"\n\tmov ebx,0"
"\n\tmov ecx,[ebp+8]"
"\n\tmov edx,1"
"\n\tint 0x80"
"\n\tret"
"\n.att_syntax" ;return to g++'s at&t syntax
);
我正在 x86_64 Linux 操作系统上使用 G++
开发这个
我正在使用它来编译:
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
不同。您可以在此 table 中了解 64 位系统调用。 sys_write
和 sys_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_write
。 sys_write
需要指向包含字符的缓冲区的地址,而不是字符本身。
这是一个可以工作的代码转储:
asm(
"\n.intel_syntax noprefix"
"\n.globl _putc"
"\n_putc:"
"\n\tadd 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
"\n\tmov [rsp],rdi" // Move the character passed in RDI to memory(stack) for sys_write
"\n\tmov eax,1" // With 64-bit syscalls RAX = 1 for sys_write
"\n\txor edi,edi" // 1st parameter to syscall is in RDX. FD = 0
"\n\tlea rsi,[rsp]" // We need to pass pointer to memory containing our char
"\n\tmov edx,1" // RDX = 1 character to print. Could reuse EAX instead of 1 for src
"\n\tsyscall" // Make syscall (using syscall instead of int 0x80 for 64-bit)
"\n\tadd rsp, 8" // Restore stack to state right after function entry
"\n\tret"
"\n.att_syntax prefix" //return to g++'s at&t syntax
);
我正在尝试编写自己的 I/O 库,所以第一步只是能够输出一个字符。到目前为止,这只需要在 Linux 上起作用。我在 x86(使用 Intel 语法)中编写了我认为是可靠的 _putc:
标签,并将其放入 "io.h",然后从我的主文件 "test.cpp" 中调用它。这是test.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:"
"\n\tpush [ebp]"
"\n\tmov ebp,esp"
"\n\tmov eax,4"
"\n\tmov ebx,0"
"\n\tmov ecx,[ebp+8]"
"\n\tmov edx,1"
"\n\tint 0x80"
"\n\tret"
"\n.att_syntax" ;return to g++'s at&t syntax
);
我正在 x86_64 Linux 操作系统上使用 G++
开发这个我正在使用它来编译:
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
不同。您可以在此 table 中了解 64 位系统调用。 sys_write
和 sys_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_write
。 sys_write
需要指向包含字符的缓冲区的地址,而不是字符本身。
这是一个可以工作的代码转储:
asm(
"\n.intel_syntax noprefix"
"\n.globl _putc"
"\n_putc:"
"\n\tadd 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
"\n\tmov [rsp],rdi" // Move the character passed in RDI to memory(stack) for sys_write
"\n\tmov eax,1" // With 64-bit syscalls RAX = 1 for sys_write
"\n\txor edi,edi" // 1st parameter to syscall is in RDX. FD = 0
"\n\tlea rsi,[rsp]" // We need to pass pointer to memory containing our char
"\n\tmov edx,1" // RDX = 1 character to print. Could reuse EAX instead of 1 for src
"\n\tsyscall" // Make syscall (using syscall instead of int 0x80 for 64-bit)
"\n\tadd rsp, 8" // Restore stack to state right after function entry
"\n\tret"
"\n.att_syntax prefix" //return to g++'s at&t syntax
);