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

Cyr*_*yro 13 assembly gnu-assembler ld binutils

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

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

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

我在x86机器上运行Linux.

Mat*_*ery 20

要成功将libc与动态链接一起使用,至少需要做三件事:

  1. /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!
$
Run Code Online (Sandbox Code Playgroud)

  • 我来这里寻找对 elf64 执行相同操作的指令,发现上述指令可以将 ld-linux.so.2 的引用更改为 ld-linux-x86_64.so.2。谢谢! (2认同)

Cir*_*四事件 5

如果main在程序集中定义

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

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

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

您可能还需要-lgcc-lgcc_s。另请参阅:我真的需要 libgcc 吗?

如果_start在程序集中定义

如果我定义了_start,那么来自 glibc 的 hello world 仅适用于:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o
Run Code Online (Sandbox Code Playgroud)

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