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

发布于 2024-09-16 07:21:16 字数 322 浏览 12 评论 0原文

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

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

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

我在 x86 机器上运行 Linux。

As an exercise to learn more precisely how c programs work and what minimum level of content must exist for a program to be able to use libc, I've taken it upon myself to attempt to program primarily in x86 assembly using gas and ld.

As a fun little challenge, I've successfully assembled and linked several programs linked to different self-made dynamic libraries, but I have failed to be able to code a program from scratch to use libc function calls without directly using gcc.

I understand the calling conventions of individual c library functions, and have thoroughly inspected programs compiled out of gcc through use of objdump and readelf, but haven't gotten anywhere as far as what information to include in a gas assembly file and what parameters to invoke in ld to successfully link to libc. Anyone have any insight to this?

I'm running Linux, on an x86 machine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

葬﹪忆之殇 2024-09-23 07:21:16

要成功使用带有动态链接的 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!
$

There are at least three things that you need to do to successfully use libc with dynamic linking:

  1. Link /usr/lib/crt1.o, which contains _start, which will be the entry point for the ELF binary;
  2. Link /usr/lib/crti.o (before libc) and /usr/lib/crtn.o (after), which provide some initialisation and finalisation code;
  3. Tell the linker that the binary will use the dynamic linker, /lib/ld-linux.so.

For example:

$ 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!
$
冰葑 2024-09-23 07:21:16

如果您在汇编中定义main

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

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

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

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

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

collect2 是 GCC 用作链接器前端的内部可执行文件-end,其接口与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 链接时才能工作?

If you define main in assembly

Matthew's answer does a great job of telling you the minimum requirements.

Let me show you how how to find those paths in your system. Run:

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

and then pick up the files Matthew mentioned.

gcc -v gives you the exact linker command GCC uses.

collect2 is the internal executable GCC uses as a linker front-end, which has a similar interface to ld.

In Ubuntu 14.04 64-bit (GCC 4.8), I ended up with:

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

You might also need -lgcc and -lgcc_s. See also: Do I really need libgcc?

If you define _start in assembly

If I defined the _start, the hello world from glibc worked with just:

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

I'm not sure if this is robust, i.e. if the crt initializations can be safely skipped to invoke glibc functions. See also: Why does an assembly program only work when linked with crt1.o crti.o and crtn.o?

蓝天白云 2024-09-23 07:21:16

我认为这样的事情应该可行:

  1. 制作一个简单的C程序
  2. gcc -S file.c
  3. edit file.s
  4. gas file.s
  5. ld file.o -lc crt1.o -o myprog

I think something like this should work:

  1. make a simple C program
  2. gcc -S file.c
  3. edit file.s
  4. gas file.s
  5. ld file.o -lc crt1.o -o myprog
煮酒 2024-09-23 07:21:16

如果您确实使用 _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)

If you do use _start instead of main (as mentioned in some of the comments above), you'll also need to change the way the program exits, or you'll get a seg fault:

            .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!"

On 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

Also, one easy way to find out what the dynamic linker is on your system is to compile a small C program and then run ldd on the binary:

test.c:

int main() { return 0; }

Compile and run ldd against executable:

$ 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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文