【问题标题】:Will the assembly program be linked with a C library?汇编程序会与 C 库链接吗?
【发布时间】:2022-01-29 16:12:33
【问题描述】:

我有以下汇编程序,它通过适当的系统调用将“hello world”打印到屏幕上:

.global _start

     .text
            _start:
            # write(1, message, 13)
            mov     $1, %rax                # system call 1 is write
            mov     $1, %rdi                # file handle 1 is stdout
            mov     $message, %rsi          # address of string to output
            mov     $13, %rdx               # number of bytes
            syscall                         # invoke operating system to do the write
    
            # exit(0)
            mov     $60, %rax               # system call 60 is exit
            xor     %rdi, %rdi              # we want return code 0
            syscall                         # invoke operating system to exit
    message:
            .ascii  "Hello, world\n"

如果我在 ubuntu 终端中发出命令 gcc hello.s,汇编代码是否会像网页中的某个人所说的那样与 C 库链接?如果是,那是为什么?汇编代码似乎没有在任何地方引用 C 代码。

【问题讨论】:

  • gccasld 的 Gnu C 编译器和包装器。 as 是汇编程序。 ld 是链接器。调用 gcc 会做预处理、编译组装和链接,具体取决于选项。
  • 这一切我都知道。我的问题是命令'gcc hello.s'是否会链接到C库?反汇编输出可执行文件有什么帮助吗?
  • 执行gcc -v hello.s,它将显示它运行的所有步骤:as 和 ld。尝试与作为 C 库的一部分的 crt1.o 链接失败。
  • 如果你以这种方式构建,它会是,但你不需要也不应该不应该。使用gcc -nostdlib -static foo.s,因为您正在定义自己的_start 并使用原始系统调用。另请参阅Assembling 32-bit binaries on a 64-bit system (GNU toolchain)(但省略 -m32)

标签: linux assembly gcc linker


【解决方案1】:

如果我在 ubuntu 终端中发出命令 gcc hello.s,汇编代码是否会像网页中的某个人所说的那样与 C 库链接?

是的。

如果是,那是为什么?

这是gcc 命令在链接时的记录默认行为,因为它主要用于 C 程序。

汇编代码似乎没有在任何地方引用 C 代码。

没关系,因为汇编器和链接器不检查是否检查。

默认行为是设置涉及 C 库、其标准启动代码和您的代码的链接。运行gcc -v hello.s可以看到详情:

/usr/lib/gcc/x86_64-linux-gnu/9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccVrfSjS.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. /tmp/cc17H49O.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o

crt*.o 文件是启动代码,-lc 链接 C 库。

在 C 程序中,标准启动代码应该是第一个运行的东西,以便初始化库并以编译的 C 代码所期望的方式设置各种东西。因此它定义了用作程序入口点的_start 标签,它包含对名为main 的符号的调用。因此,只有在您提供的代码确实定义了一个名为main的符号,并且没有定义了一个名为_start的符号(因为不允许是两个,否则链接器将不知道使用哪个)。无论哪种情况,您的代码都不是这种情况。

如果您想要一些其他行为,它由命令行开关决定,而不是由您的代码内容决定。 -nostdlib 告诉链接器不要包含 C 库及其启动代码;在这种情况下,您的代码必须提供 _start 并且不需要 main。因此,如果您使用gcc -nostdlib -no-pie foo.s 进行编译,您的代码将成功编译并运行。

(-no-pie 选项是必需的,因为您的代码在mov $message, %rsi 中使用绝对静态地址作为立即数。这些限制为 32 位,因此如果代码要重新定位到64 位地址空间。更好的做法是使用 RIP 相对寻址:lea message(%rip), %rsi,然后您就不需要 -no-pie。您也可以使用带有 64 位立即数的 mov 的形式,@ 987654339@,但它使用了更多的代码字节。)

【讨论】:

  • 好的,谢谢。我对启动代码、_start 和 main 有一个模糊的想法,现在你的回答让我明白了。
  • 顺便说一句,-static 暗示-no-pie,所以gcc -nostdlib -static 是您绝对想要链接它的代码的好选择,没有动态的任何东西。 (或使用所有 3 个选项)。但是-no-pie 也会让 gcc/ld 在没有库被链接的情况下生成一个静态可执行文件,所以你的方式也很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-20
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
  • 1970-01-01
  • 2013-04-22
  • 1970-01-01
相关资源
最近更新 更多