17

作为一个练习,以更准确地了解 c 程序如何工作以及程序必须存在什么最低级别的内容才能使程序能够使用 libc,我自己尝试使用 gas 和 ld 主要在 x86 汇编中进行编程。

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

我了解各个 c 库函数的调用约定,并且已经通过使用 objdump 和 readelf 彻底检查了从 gcc 编译出来的程序,但还没有得到任何地方,至于要在 gas 汇编文件中包含哪些信息以及要调用哪些参数在 ld 中成功链接到 libc。有人对此有任何见解吗?

我在 x86 机器上运行 Linux。

4

4 回答 4

23

要成功使用带有动态链接的 libc,您至少需要做三件事:

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

例如:

$ 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!
$
于 2010-08-26T23:56:02.367 回答
5

如果您main在程序集中定义

马修的回答很好地告诉了您最低要求。

让我向您展示如何在系统中找到这些路径。跑:

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

然后拿起马修提到的文件。

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。另请参阅:我真的需要 libgcc 吗?

如果您_start在程序集中定义

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

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

我不确定这是否稳健,即是否crt可以安全地跳过初始化以调用 glibc 函数。另请参阅:为什么汇编程序仅在与 crt1.o、crti.o 和 crtn.o 链接时才能工作?

于 2015-06-08T09:40:18.453 回答
1

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

  1. 做一个简单的C程序
  2. gcc -S 文件.c
  3. 编辑文件.s
  4. 气体文件.s
  5. ld 文件.o -lc crt1.o -o myprog
于 2010-08-26T19:26:22.327 回答
1

如果您确实使用_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在二进制文件上运行:

测试.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)
于 2019-02-26T00:36:52.693 回答