当对象文件和静态库中相同的符号共存时,链接器不会发出多个定义错误

Fr0*_*Bit 4 c linker

给定一个void some_func()在静态库中具有签名的编译函数,以及另一个void some_func()在目标文件中具有相同签名的函数,人们期望当它们将它们链接在一起时,应该发生"多重定义"错误.

但这种情况并非如此.据我所见,链接器(使用GCC和MSVC工具链测试)选择驻留在目标文件中的实现,而不会发出任何错误或警告.

鉴于以下POC:

somelib.h

#ifndef _SOMELIB_H_
#define _SOMELIB_H_

void some_func();

#endif /* _SOMELIB_H_ */
Run Code Online (Sandbox Code Playgroud)

somelib.c

#include "somelib.h"
#include <stdio.h>

void some_func()
{
    printf("some_func in library\n");
}
Run Code Online (Sandbox Code Playgroud)

troublingheader.h

#ifndef _TROUBLING_HEADER_H_
#define _TROUBLING_HEADER_H_

void some_func();

#endif /* _TROUBLING_HEADER_H_ */
Run Code Online (Sandbox Code Playgroud)

troublingsource.c

#include "troublingheader.h"
#include <stdio.h>

void some_func()
{
    printf("Troubling func\n");
}
Run Code Online (Sandbox Code Playgroud)

main.c中

#include <stdio.h>
#include "somelib.h"

int main()
{
    some_func();
}
Run Code Online (Sandbox Code Playgroud)

还有一个简单的Makefile来帮助构建(首先创建tmp文件夹):

tmp/wat.exe: tmp/libsomelib.a tmp/main.c.o tmp/troublingsource.c.o
    gcc -static -static-libgcc -Ltmp -otmp/wat.exe tmp/main.c.o tmp/troublingsource.c.o -lsomelib

tmp/main.c.o:
    gcc -Wall -Wextra -c -g -O0 src/main.c -o tmp/main.c.o

tmp/troublingsource.c.o:
    gcc -Wall -Wextra -c -g -O0 src/troublingsource.c -o tmp/troublingsource.c.o

tmp/somelib.o:
    gcc -Wall -Wextra -c -g -O0 src/somelib.c -o tmp/somelib.c.o

tmp/libsomelib.a: tmp/somelib.o
    ar rcs tmp/libsomelib.a tmp/somelib.c.o
Run Code Online (Sandbox Code Playgroud)

当运行最终可执行文件时,如上所述显示内容"Troubling func".

有人能解释我为什么会这样吗?

Mik*_*han 8

对于由GCC调用的GNU链接器ld,您观察到的内容由以下几点解释.

  • 链接序列中的目标文件无条件地添加到链接中,无论它是否包含程序需要的符号.

  • 静态库是目标文件的存档以及链接器可以检查的"目录".

  • 默认情况下,链接序列中静态库中的目标文件不会无条件地添加到链接中.链接器仅搜索静态库,以查找已经添加到链接中的目标文件已经观察到但未定义的符号的定义.只有在提供此类符号的定义时,才从库中提取目标文件并将其添加到链接中.仅当链接器已经需要它提供的某些定义时才会添加 它.

为避免混淆,我们将更改输出消息:

"some_func in library\n"
Run Code Online (Sandbox Code Playgroud)

至:

"some_func in somelib.o"
Run Code Online (Sandbox Code Playgroud)

然后演示这些点,解释你所看到的:

情况1

链接troublingsource.o本身和somelib.o静态库.

联动序列:main.o,troublingsource.o,libsomelib.a

gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
gcc -o test main.o troublingsource.o -L. -lsomelib
./test
Troubling func
Run Code Online (Sandbox Code Playgroud)

这里:

  • main.o 无条件地添加到链接.
  • 符号main被定义为main.o
  • 符号some_func被引用但未定义main.o
  • troublingsource.o 无条件地添加到链接
  • some_func之前引用但未定义的符号被定义为troublingsource.o.
  • 没有未解决的参考文献.libsomelib.a甚至没有被搜查过.

案例2

链接someblib.o本身和troublingsource.o静态库.

联动序列:main.o,somelib.o,libtroublingsource.a

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
ar rcs libtroublingsource.a troublingsource.o
gcc -o test main.o somelib.o -L. -ltroublingsource
./test
some_func in somelib.o
Run Code Online (Sandbox Code Playgroud)

这里:

  • main.o 无条件地添加到链接.
  • 符号main被定义为main.o
  • 符号some_func被引用但未定义main.o
  • someblib.o 无条件地添加到链接
  • some_func之前引用但未定义的符号被定义为somelib.o.
  • 没有未解决的参考文献.libtroublingsource.a甚至没有被搜查过.

案例3

链接someblib.otroublingsource.o来自单独的静态库.

联动序列:main.o,libsomelib.a,libtroublingsource.a

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
ar rcs libtroublingsource.a troublingsource.o
gcc -o test main.o -L. -lsomelib -ltroublingsource
./test
some_func in somelib.o
Run Code Online (Sandbox Code Playgroud)

这里:

  • main.o 无条件地添加到链接.
  • 符号main被定义为main.o
  • 符号some_func被引用但未定义main.o
  • libsomeblib.a 搜索了提供定义的目标文件 some_func
  • some_func在成员somelib.o中找到了一个定义libsomelib.a
  • somelib.olibsomelib.a链接中提取并添加到链接中.
  • 没有未解决的参考文献.libtroublingsource.a甚至没有被搜查过.

案例4

链接someblib.o本身和troublingsource.o自身.

联动序列:main.o,somelib.o,troublingsource.o

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
gcc -o test main.o somelib.o troublingsource.o
troublingsource.o: In function `some_func':
troublingsource.c:(.text+0x0): multiple definition of `some_func'
somelib.o:somelib.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

这里:

  • main.o 无条件地添加到链接.
  • 符号main被定义为main.o
  • 符号some_func被引用但未定义main.o
  • someblib.o 无条件地添加到链接
  • some_func之前引用但未定义的符号被定义为somelib.o
  • troublingsource.o 无条件地添加到链接
  • 已经some_func定义的符号somelib.o再次被定义troublingbsource.o.错误.

案例5

someblib.o从静态库链接.

连锁顺序: libsomelib.a main.o

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
gcc -o test -L. -lsomelib main.o
main.o: In function `main':
main.c:(.text+0xa): undefined reference to `some_func'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

这里:

  • libsomelib.a在将任何目标文件添加到链接之前遇到过.此时没有引用符号但未定义,因此libsomelib.a 甚至没有搜索,
  • main.o 无条件地添加到链接.
  • 符号main被定义为main.o
  • 符号some_func被引用但未定义main.o
  • 没有其他目标文件或库可以链接.some_func 终于是一个未定义的参考.错误.