如何在不使用gcc的情况下将使用C标准库的气体组件程序与ld链接?

时间:2010-08-26 17:48:13

标签: assembly ld gas binutils

作为一项练习,为了更精确地学习c程序如何工作以及程序能够使用libc必须具有的最低内容水平,我自己尝试使用gas和x86主要编程。 LD

作为一个有趣的小挑战,我已成功组装并链接了几个链接到不同自制动态库的程序,但是我无法从头开始编写程序以使用libc函数调用而无需直接使用gcc。

我理解单个c库函数的调用约定,并通过使用objdump和readelf彻底检查了gcc编译的程序,但是在气体汇编文件中包含哪些信息并没有得到什么要在ld中调用以成功链接到libc的参数。有人对此有任何见解吗?

我在x86机器上运行Linux。

4 个答案:

答案 0 :(得分:18)

使用动态链接成功使用libc至少需要做三件事:

  1. 链接/usr/lib/crt1.o,其中包含_start,它将是ELF二进制文件的入口点;
  2. 链接/usr/lib/crti.o(在libc之前)和/usr/lib/crtn.o(之后),它们提供了一些初始化和最终化代码;
  3. 告诉链接器二进制文件将使用动态链接器/lib/ld-linux.so
  4. 例如:

    $ cat hello.s
     .text
     .globl main
    main:
     push %ebp
     mov %esp, %ebp
     pushl $hw_str
     call puts
     add $4, %esp
     xor %eax, %eax
     leave
     ret
    
     .data
    hw_str:
     .asciz "Hello world!"
    
    $ as -o hello.o hello.s
    $ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc hello.o /usr/lib/crtn.o
    $ ./hello
    Hello world!
    $
    

答案 1 :(得分:3)

如果您在汇编中定义main

马修的答案很好地告诉你最低要求。

让我告诉您如何在系统中找到这些路径。运行:

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'

然后拿起Matthew提到的文件。

gcc -v为您提供GCC使用的确切链接器命令。

collect2是GCC用作链接器前端的内部可执行文件,它具有与ld类似的接口。

在Ubuntu 14.04 64位(GCC 4.8)中,我最终得到了:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
  /usr/lib/x86_64-linux-gnu/crt1.o \
  /usr/lib/x86_64-linux-gnu/crti.o \
  -lc hello_world.o \
  /usr/lib/x86_64-linux-gnu/crtn.o

您可能还需要-lgcc-lgcc_s。另见:Do I really need libgcc?

如果您在汇编中定义_start

如果我定义了_start,那么来自glibc的hello world只能使用:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o

我不确定这是否健壮,即是否可以安全地跳过crt初始化来调用glibc函数。另见:Why does an assembly program only work when linked with crt1.o crti.o and crtn.o?

答案 2 :(得分:0)

如果您确实使用_start而不是main(如上面的一些评论中所述),则还需要更改程序退出的方式,否则将得到一个段错误:

            .text
            .globl    _start
_start:     
            mov       $hw_str, %rdi
            call      puts
            movl      $0,%ebx   # first argument: exit code.
            movl      $1,%eax   # system call number: sys_exit.
            int       $0x80     # call kernel.

            .data
hw_str:     .asciz "Hello world!"

在Kubuntu 18.04.2(gcc(Ubuntu 7.3.0-27ubuntu1〜18.04)7.3.0):

$ as -o hello.o hello.s
$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello hello.o -lc

另外,找出系统上的动态链接器的一种简单方法是编译一个小型C程序,然后在二进制文件上运行ldd

test.c:

int main() { return 0; }

编译并针对可执行文件运行ldd:

$ gcc -o test test.c
$ ldd test
    linux-vdso.so.1 (0x00007ffd0a182000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff24d8e6000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff24ded9000)

答案 3 :(得分:-1)

我认为这样的事情应该有效:

  1. 制作一个简单的C程序
  2. gcc -S file.c
  3. 编辑文件。
  4. gas file.s
  5. ld file.o -lc crt1.o -o myprog
相关问题