测试平台在Linux 32位上。 (但也欢迎Windows 32位上的某些解决方案)
这是一个c代码段:
int a = 0;
printf("%d\n", a);
如果我使用gcc生成汇编代码
gcc -S test.c
然后我会得到:
movl $0, 28(%esp)
movl 28(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
leave
ret
这个汇编代码需要链接到libc才能工作(因为调用printf)
我的问题是:
是否可以在不使用libc的情况下使用系统调用仅将显式转换为asm?
像这样:
pop ecx
add ecx,host_msg-host_reloc
mov eax,4
mov ebx,1
mov edx,host_msg_len
int 80h
mov eax,1
xor ebx,ebx
int 80h
直接调用int 80h软件中断。
有可能吗?如果是这样,这个问题上有没有工具?
谢谢!
答案 0 :(得分:7)
不是来自该源代码。编译器无法将对printf()
的调用转换为对write
系统调用的调用 - printf()
库函数包含系统调用中不存在的大量逻辑(例如处理格式字符串并将整数和浮点数转换为字符串)。
可以直接生成系统调用,但只能使用内联汇编。例如,要生成对_exit(0)
的调用(与exit()
不完全相同!),您可以写:
#include <asm/unistd.h>
...
int retval;
asm("int $0x80" : "=a" (retval) : "a" (__NR_exit), "b" (0));
有关GCC内联汇编的更多信息,特别是我在这里用来将变量映射到寄存器的约束,请阅读GCC Inline Assembly HOWTO。它相当陈旧,但仍然完全相关。
请注意,执行此操作不推荐。系统调用的确切调用约定(例如,哪些寄存器用于调用号和参数,如何返回错误,等)在不同的体系结构,操作系统甚至32位之间都是不同的和64位x86。以这种方式编写代码将使维护变得非常困难。
答案 1 :(得分:6)
您当然可以将C代码编译为汇编而无需链接到libc,但是您无法使用C库函数。 Libc的全部目的是提供从C库函数到Linux系统调用(或Windows,或者您正在使用的任何系统)的接口。因此,如果您不想使用libc,则必须将自己的包装器写入系统调用。
答案 2 :(得分:2)
如果您编译一些不使用C library中任何函数的C代码(例如,不使用printf
或malloc
等等......)的独立性GCC编译器的模式(即-ffreestanding标志为gcc
),你需要调用一些汇编程序函数(来自其他一些对象或库)或使用asm
指令(如果不进行系统调用,你将无法进行任何类型的输入输出。)
另请阅读与您的内核相关的Assembly HowTo,x86 calling conventions和ABI(可能是x86-64 ABI)并完全理解system calls是什么,开始使用syscalls(2)以及VDSO是什么(int 80不是最近制作系统调用的方法,SYSENTER
通常更好)。研究一些libc的源代码,特别是MUSL libc(源代码非常易读)。
在Windows上(这不是免费软件,我不知道),问题可能要困难得多:我不确定系统调用级别是否完全记录完毕。
libffi使您可以从C调用任意函数。您还可以从dlsym(3)转换函数指针。您可以考虑JIT种技术(例如libjit,GNU lightning,asmjit等等。)