作为一种练习,可以更准确地学习C程序的工作原理以及能够使用LIBC必须存在的最低内容水平,因此我自己想尝试使用GAS和LD在X86组装中进行编程。

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

我了解单个C库功能的呼叫惯例,并通过使用Objdump和Readfelf彻底检查了从GCC中汇总的程序,但尚未到达燃气组装文件中的哪些信息以及可以调用哪些参数在LD中成功链接到LIBC。有人对此有任何洞察力吗?

我正在X86机器上运行Linux。

有帮助吗?

解决方案

您至少需要做三件事,以成功地使用具有动态链接的LIBC:

  1. 关联 /usr/lib/crt1.o, , 其中包含 _start, ,这将是精灵二进制的切入点;
  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!
$

其他提示

如果定义 main 在组装中

Matthew的答案非常出色地告诉您最低要求。

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

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

然后拿起Matthew提到的文件。

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

收集2 是内部可执行性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链接时只能使用组装程序?

如果您确实使用 _start 代替 main (如上所述的某些评论中所述),您还需要更改程序退出的方式,否则您将获得SEG错误:

            .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

另外,一种简单的方法来找出系统上的动态链接器是编译小型程序然后运行的一种简单方法 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)

我认为这样的事情应该起作用:

  1. 制作一个简单的C程序
  2. gcc -s file.c
  3. 编辑文件
  4. 煤气文件
  5. ld file.o -lc crt1.o -o myprog
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top