【问题标题】:What could be causing linking errors when compiling in an Alpine Docker?在 Alpine Docker 中编译时可能导致链接错误的原因是什么?
【发布时间】:2019-11-09 01:28:53
【问题描述】:

我正在尝试在从 Alpine 3.7 基础映像构建的 docker 容器中编译一个程序。该程序使用argp.h,并将其包含为#include <argp.h>。我已经安装了 argp-standalone 并验证它正在制作到图像上。文件argp.h 位于usr/include,但是当我使用以下命令编译我的程序时:

gcc -W -Wall -Wextra -I/usr/include   -c -o progname.o progname.c
gcc -largp -o progname progname.o

我收到以下错误:

progname.o: In function `parse_opt':
progname.c:(.text+0x4c9): undefined reference to `argp_failure'
progname.c:(.text+0x50f): undefined reference to `argp_failure'
progname.c:(.text+0x555): undefined reference to `argp_failure'
progname.c:(.text+0x59b): undefined reference to `argp_failure'
progname.c:(.text+0x5ce): undefined reference to `argp_error'
progname.c:(.text+0x5f4): undefined reference to `argp_error'
progname.o: In function `main':
progname.c:(.text+0x1397): undefined reference to `argp_parse'
collect2: error: ld returned 1 exit status
make: *** [Makefile:9: progname] Error 1

我有:

  • 确保图像上的argp.h 版本实际上包含argp_failureargp_parseargp_error 函数。
  • 尝试将 argp.h 移动到机器上的不同位置(例如,移动到进行编译的同一目录,移动到 /usr/lib
  • 尝试使用-l-L 进行编译。

镜像中还安装的相关包是build-basemakegcc。 在 Ubuntu 映像上编译时,即使没有 -largp-I/usr/include 标志,这些相同的命令也可以正常工作。 在 Alpine 图像中可能会发生什么不同的情况而导致此功能不起作用?

编辑

根据@Pablo 的评论,我现在将其编译如下:

gcc -W -Wall -Wextra -I/usr/include -L/usr/lib -c -o progname.o progname.c
gcc -largp -o progname progname.o

在验证静态库libargp.a 位于/usr/lib 之后。但是,同样的问题仍然存在。

编辑 2

编译如下(再次按照@Pablo的建议)解决了我遇到的错误:

gcc -W -Wall -Wextra -I/usr/include -L/usr/lib -c -o progname.o progname.c
gcc -o progname progname.o /usr/lib/libargp.a

但是,我仍然很好奇,为什么在使用完全相同的库和指令时,在 Ubuntu 映像中编译时会无法在 Alpine 映像中编译。

【问题讨论】:

  • 链接时头文件无关。您需要共享库 (.so) 或静态库 (.a)。假设是共享库,需要找到libargp.so的存放位置,并以-L传递目录的路径。如果您没有这两个文件(libargp.solibargp.a),则无法链接到 -largp
  • 好的,谢谢@Pablo 的回复。静态库libargp.a 位于/usr/lib,但是在使用-L 标志传递此目录后问题仍然存在。
  • 如果这是一个静态库,尝试像这样链接:gcc -o progname progname.o /usr/lib/libargp.a

标签: c docker linker alpine


【解决方案1】:

我仍然很好奇,为什么在使用完全相同的库和指令时,在 Ubuntu 映像中编译时会无法在 Alpine 映像中编译。

Alpine 上的链接错误的原因可能有点令人惊讶,实际上并不是 Alpine 所特有的。

虽然链接失败:

gcc -largp -o progname progname.o

这行得通:

gcc -o progname progname.o -largp

原因是传递给链接器的参数顺序,它与链接算法有关。通常,在链接命令行中首先指定对象(可能还有用户的静态库),然后是使用-l 的库。 Eli Bendersky 的文章Library order in static linking 中对标准链接器算法进行了完美的解释:

对象文件和库在命令行上以从左到右的特定顺序提供。这是链接顺序。以下是链接器的作用:

  • 链接器维护一个符号表。这个符号表做了很多事情,但其中有两个列表:
    • 到目前为止遇到的所有对象和库导出的符号列表。
    • 遇到的对象和库请求导入但尚未找到的未定义符号列表。
  • 当链接器遇到新的目标文件时,它会查看:
    • 它导出的符号:这些符号被添加到上面提到的导出符号列表中。如果任何符号在未定义列表中,它就会从那里删除,因为它现在已经找到了。如果导出列表中已经有任何符号,我们会收到“多重定义”错误:两个不同的对象导出相同的符号,链接器会混淆。
    • 它导入的符号:这些符号被添加到未定义符号列表中,除非它们可以在导出符号列表中找到。
  • 当链接器遇到一个新库时,事情就变得更有趣了。链接器遍历库中的所有对象。对于每一个,它首先查看它导出的符号。
    • 如果它导出的任何符号在未定义列表中,则将对象添加到链接中并执行下一步。否则,跳过下一步。
    • 如果对象已添加到链接中,则按上述方式处理 - 其未定义和导出的符号将添加到符号表中。
    • 最后,如果库中的任何对象已包含在链接中,则会再次重新扫描库 - 被包含的对象导入的符号可能会在同一库中的其他对象中找到。

-largp 首先出现时,链接器不会在链接过程中包含它的任何对象,因为它还没有任何未定义的符号。如果静态库是通过路径提供的,而不是-l,那么它的所有对象都会被添加到链接过程中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    • 1970-01-01
    相关资源
    最近更新 更多