【问题标题】:Linking a C program directly with ld fails with undefined reference to `__libc_csu_fini`使用 ld 直接链接 C 程序失败,未定义对 `__libc_csu_fini` 的引用
【发布时间】:2011-10-03 03:13:45
【问题描述】:

我正在尝试在 Linux 下编译 C 程序。但是,出于好奇,我尝试手动执行一些步骤:我使用:

  • 生成汇编代码的 gcc 前端
  • 然后运行 ​​GNU 汇编程序以获取目标文件
  • 然后将其与 C 运行时链接以获得工作可执行文件。

现在我被链接部分卡住了。

程序是一个非常基础的“Hello world”:

#include <stdio.h>
int main() {
   printf("Hello\n");
   return 0;
}

我使用以下命令生成汇编代码:

gcc hello.c -S -masm=intel

我告诉 gcc 在使用 Intel 语法编译和转储汇编代码后退出。

然后我使用 GNU 汇编器来生成目标文件:

as -o hello.o hello.s

然后我尝试使用 ld 生成最终的可执行文件:

ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello

但我不断收到以下错误消息:

/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'

符号__libc_csu_fini/init 似乎是glibc 的一部分,但我在任何地方都找不到它们!我尝试静态链接 libc(针对/usr/lib/libc.a),结果相同。

可能是什么问题?

【问题讨论】:

  • objdump 为您的 crt1.o 显示什么。我的显示那些符号 -
  • 修复目标文件顺序并将/usr/lib/crti.o 添加到混合中可以让我通过链接器,但生成的可执行文件将无法运行 (bash: ./hello: No such file for directory),因此缺少其他内容。
  • 在汇编中手动编写的代码相同:stackoverflow.com/questions/3577922/…

标签: c gcc linker glibc ld


【解决方案1】:

/usr/lib/libc.so 是一个链接器脚本,它告诉链接器拉入共享库/lib/libc.so.6 和非共享部分/usr/lib/libc_nonshared.a

__libc_csu_init__libc_csu_fini 来自 /usr/lib/libc_nonshared.a。未找到它们是因为对非共享库中的符号的引用需要出现在链接器行上定义它们的存档之前之前。在您的情况下,/usr/lib/crt1.o(引用它们)出现在 /usr/lib/libc.so(将它们拉入)之后,所以它不起作用。

修复链接行上的顺序会让你走得更远,但你可能会遇到一个新问题,__libc_csu_init__libc_csu_fini(现已找到)找不到 _init_fini。为了调用 C 库函数,您还应该链接/usr/lib/crti.o(在crt1.o 之后,但 C 库之前)和/usr/lib/crtn.o C 库之后) ,其中包含初始化和完成代码。

添加这些应该会给你一个成功链接的可执行文件。它仍然不起作用,因为它使用动态链接的 C 库而没有指定动态链接器是什么。您还需要告诉链接器,例如 -dynamic-linker /lib/ld-linux.so.2(至少对于 32 位 x86;标准动态链接器的名称因平台而异)。

如果你做了所有这些(基本上按照 Rob 的回答),你会得到一些在简单情况下有效的东西。但是您可能会遇到更复杂的代码的更多问题,因为 GCC 提供了一些自己的库例程,如果您的代码使用某些功能可能需要这些例程。这些将被埋在 GCC 安装目录的深处......

您可以通过使用-v 选项(它将向您显示它在运行时调用的命令)或-### 选项(它只打印它会打印的命令)来查看gcc 正在做什么运行,使用所有参数引号,但实际上不运行任何东西)。除非您知道它通常通过其自己的组件之一,collect2(用于在正确的位置粘合 C++ 构造函数调用)间接调用 ld,否则输出将令人困惑。

【讨论】:

  • 太棒了。解释清楚、简明扼要、精简到要领。我一直在 ld 丛林中导航,将直接链接的可执行文件从 Solaris 移植到 Linux 并最终来到这里,因为当我试图解决“权限被拒绝”错误时,我不得不交换链接器行上的对象顺序执行刚刚链接的可执行文件时。对于在编译或链接可执行文件后遇到“权限被拒绝”的任何其他人,票证可能正在指定动态链接器,正如 Matthew 解释的那样。这是给我的。
【解决方案2】:

我发现 another post 包含一条线索:-dynamic-linker /lib/ld-linux.so.2

试试这个:

$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$ 

【讨论】:

    【解决方案3】:

    假设对gcc -o hello hello.c 的正常调用产生了一个工作构建,运行这个命令:

    gcc --verbose -o hello hello.c
    

    gcc 会告诉你它是如何连接事物的。这应该可以让您很好地了解在链接步骤中可能需要考虑的所有内容。

    【讨论】:

      【解决方案4】:

      在 Ubuntu 14.04 (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 \
        -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
        -lc -lgcc -lgcc_s \
        hello.o \
        /usr/lib/x86_64-linux-gnu/crtn.o
      

      虽然它们可能不是必需的,但您也应该链接到 -lgcc-lgcc_s,因为 GCC 可能会调用这些库中存在的函数来执行您的硬件未本地实现的操作,例如long long int 32 位操作。另见:Do I really need libgcc?

      我必须补充:

        -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
      

      因为默认链接描述文件不包含该目录,而那是libgcc.a 所在的位置。

      正如 Michael Burr 所说,您可以使用gcc -v 找到路径。更准确地说,您需要:

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

      【讨论】:

        【解决方案5】:

        这是我在 ubuntu 11.10 上修复它的方式:

        apt-get remove libc-dev
        

        选择“是”以删除所有软件包,但复制列表以在之后重新安装。

        apt-get install libc-dev
        

        【讨论】:

          【解决方案6】:

          如果您运行的是 64 位操作系统,您的 glibc(-devel) 可能会损坏。通过查看thisthis,您可以找到以下三种可能的解决方案:

          1. 将 lib64 添加到 LD_LIBRARY_PATH
          2. 使用 lc_noshared
          3. 重新安装 glibc-devel

          【讨论】:

            【解决方案7】:

            由于您是手动执行链接过程,因此您忘记了链接 C 运行时初始化程序,或者它所调用的任何内容。

            为了不详细说明应该为您的平台链接的位置和内容,在获取您的 intel asm 文件后,使用 gcc 生成(编译和链接)您的可执行文件。

            只需执行gcc hello.c -o hello 即可。

            【讨论】:

              【解决方案8】:

              拿去吧:

                  $ echo 'main(){puts("ok");}' > hello.c
                  $ gcc -c hello.c -o hello.o
                  
                  $ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
              -dynamic-linker /lib/ld-linux.so.2 -lc
                  
                  $ ./hello.exe
                  ok
              

              glibc 配置了 --prefix=/usr

              时,/usr/lib/crt*.o 的路径将

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多