在目标文件之后列出库
仅链接目标文件和库时,在目标文件之后列出库:
gcc -o Server1.exe -Wall -Wunused -ansi -pedantic -ggdb Server1.o Util.o -lpthread -lm
当链接命令包含源文件时,在库之前列出源文件和目标文件(如果有)。因此,-l 选项位于命令行的末尾。指定库所在位置的相关-L 选项应位于指定库的-l 选项之前。
次要问题是:
为什么会起作用?
当 C 编译器调用链接器时,它告诉链接器拉入一些名称类似于 crt0.o 的系统目标文件,并告诉它查找符号 main(或者可能是 _main(),具体取决于本地命名约定)。它还按照您在命令行中指定的顺序提供目标文件和库。当它遇到一个目标文件时,链接器会记录它提供的定义,以及它所做的不满意的引用。当它遇到一个库时,它会扫描该库以查看它是否可以满足任何不满足的引用。如果该库可以提供任何尚未满足的引用,那么它会在“可执行文件”中包含该库的(相关部分)。对于共享库,链接器确保库将在运行时加载。对于静态库,链接器包括库中至少满足一个引用的目标文件,重新扫描直到没有其他引用可以满足。如果库不满足引用,则将其忽略。该过程完成后,如果任何引用仍未满足,您会收到错误消息。
因此,在您的场景中,您在 Server1.o 或 Util.o 之前有 -lpthread。由于-lpthread 没有提供main 函数并且这是唯一相关的不满足符号,因此它被忽略了。数学库-lm 也可能已被忽略,或者它可能是一个空存根,用于保存为数学库与主 C 库分开的其他系统设计的代码。然后链接器读取您的目标文件,并找到对pthread_create() 的引用。之后扫描 C 库 -lc (libc.so) 时,发现符号满足除 pthread_create 之外的所有内容。
当库在目标文件之后列出时,链接器在扫描-lpthread 时就知道它需要pthread_create,并确保在运行时加载共享库。
GNU ld 和 --as-needed 选项
上面的讨论本质上是平台中立的。如果您遵循“目标文件后的库”规则,您的链接器行在所有平台上都能正常工作的最大机会。
如果您在使用 GNU binutils 包,尤其是 GNU ld 命令的系统上,您可能会发现不同的行为。
来自Sourceware 的手册(如果您尝试http://www.gnu.org/software/binutils/manuals,您会被重定向到这里)包含以下信息:
--as-needed
--no-as-needed
此选项会影响--as-needed 选项后命令行中提到的动态库的 ELF DT_NEEDED 标记。通常,链接器将为命令行中提到的每个动态库添加一个 DT_NEEDED 标记,无论该库是否实际需要。 --as-needed 导致 DT_NEEDED 标记仅针对链接中的该点满足来自常规对象文件的非弱未定义符号引用的库发出,或者,如果在其他库的 DT_NEEDED 列表中找不到该库,来自另一个动态库的非弱未定义符号引用。在相关库之后出现在命令行上的目标文件或库不会影响是否根据需要查看该库。这类似于从档案中提取目标文件的规则。 --no-as-needed 恢复默认行为。
似乎不同系统的不同版本对as-needed 选项使用不同的值。虽然--no-as-needed 行为很方便,因为它允许您在命令行上以或多或少的任意顺序对库和目标文件进行排序,但这也意味着命令行上列出的所有库都在运行时加载,即使有库中没有实际使用的符号(因此--no-as-needed 等效于假设的--whether-needed-or-not 标志)。使用 --as-needed 选项是经典且可移植的行为。
有传言说,一些 Linux 发行版在过去 5 年左右(第三个千年的第二个十年上半叶,为了争论)的某个时候,将其系统上的默认行为从 --no-as-needed 更改为 --as-needed )。您可以在一些关于 SO 的问题中找到支持这一谣言的证据。