是否可以在Linux上没有链接libc的情况下将C转换为asm?

时间:2014-01-22 18:27:27

标签: c assembly compiler-construction assemblies

测试平台在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软件中断。

有可能吗?如果是这样,这个问题上有没有工具?

谢谢!

3 个答案:

答案 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代码(例如,不使用printfmalloc等等......)的独立性GCC编译器的模式(即-ffreestanding标志为gcc),你需要调用一些汇编程序函数(来自其他一些对象或库)或使用asm指令(如果不进行系统调用,你将无法进行任何类型的输入输出。)

另请阅读与您的内核相关的Assembly HowTox86 calling conventionsABI(可能是x86-64 ABI)并完全理解system calls是什么,开始使用syscalls(2)以及VDSO是什么(int 80不是最近制作系统调用的方法,SYSENTER通常更好)。研究一些libc的源代码,特别是MUSL libc(源代码非常易读)。

在Windows上(这不是免费软件,我不知道),问题可能要困难得多:我不确定系统调用级别是否完全记录完毕。

libffi使您可以从C调用任意函数。您还可以从dlsym(3)转换函数指针。您可以考虑JIT种技术(例如libjit,GNU lightningasmjit等等。)