duplicate symbol 小探

Pretty
duplicate-symbol-exploring
Tags
iOS
C++
Date
Nov 29, 2021
💡
如果清理缓存之后编译链接没有问题,但运行起来发现你的代码不是你写的代码,可以看看这篇文章。
 
创建一个默认模板的 iOS App 工程。
 
在相同的 project 下创建一个名为 alib 的 static library 的 target ,然后给主工程的 Link Binary With Libraries 添加 libalib.a ,Dependencies 添加 alib。
 
blib 类似。
 
完成后如下图所示。
 
notion image
 
然后在 alib.m 里面加两行全局变量的初始化,如下所示。
 
#import "alib.h" int demo_global_int = 1; int demo_global_int_a = 1; @implementation alib @end
 
blib.m 里也类似。
 
#import "blib.h" int demo_global_int = 2; int demo_global_int_b = 2; @implementation blib @end
 
然后修改 main.m ,通过 extern 来链接符号,打印输出。
 
extern int demo_global_int; extern int demo_global_int_a; extern int demo_global_int_b; int main(int argc, char * argv[]) { NSLog(@"%d", demo_global_int); NSLog(@"%d", demo_global_int_a); NSLog(@"%d", demo_global_int_b); return 0; }
 
运行时的输出取决于链接顺序。
 
notion image
查看链接参数里的顺序如下 -lblib -lalib ,此时输出为 1 1 2
 
然后修改顺序。
notion image
查看链接参数里的顺序如下 -lalib -lblib ,此时输出为 2 1 2
 
查看链接输出,会有如下警告
 
ld: warning: duplicate symbol '_demo_global_int' in: /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libalib.a(alib.o) /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libblib.a(blib.o)
 
这个例子的 demo_global_int_a 和 demo_global_int_b 并不是重复符号,在这里是想说明 alib.o 和 blib.o 是有同时链接进来的,另外也可以通过 LinkMap 来确认。
 
如果注释 demo_global_int_a 和 demo_global_int_b 的所有相关代码,会发现没有链接警告。这是因为链接器可以操作的最小单元为目标文件, alib.o 和 blib.o 只链接了其中一个,另一个由于没有符号引用,就不会链接了。
 
现在来看这个设置, DEAD_CODE_STRIPPING = YES ,默认 Xcode 就是开启的。
 
notion image
 
如果保留 demo_global_int_a 和 demo_global_int_b 的所有相关代码,然后设置 DEAD_CODE_STRIPPING = NO ,则原来的警告会变成错误
duplicate symbol '_demo_global_int' in: /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libalib.a(alib.o) /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libblib.a(blib.o) ld: 1 duplicate symbol for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
 
如果设置 Other Linker Flags 增加 -all_load ,则也是同样的错误。无论是否保留 demo_global_int_a 和 demo_global_int_b 的所有相关代码,以及 DEAD_CODE_STRIPPING 是否 YES 。
 
notion image
 
如果不希望影响到所有的链接,只针对个别静态库进行强制加载,则使用 -force_load 加上指定静态库路径。
 
以上,这个重复符号,包括全局变量、函数。
 
即使用了 C++ 的命名空间 namespace ,但 namespace 的名字一样,也是一样会有这个问题,示例如下。
 
将 alib.m 改为 alib.mm ,包含以下代码
 
namespace DWJ { int demo_global_int = 1; int demo_global_int_a = 1; int demo_global_func(void) { return 1; } }
 
blib.mm 类似
 
namespace DWJ { int demo_global_int = 2; int demo_global_int_b = 2; int demo_global_func(void) { return 2; } }
 
main.mm
 
namespace DWJ { extern int demo_global_int; extern int demo_global_int_a; extern int demo_global_int_b; extern int demo_global_int_b; extern int demo_global_func(void); } int main(int argc, char * argv[]) { NSLog(@"%d", DWJ::demo_global_int); NSLog(@"%d", DWJ::demo_global_int_a); NSLog(@"%d", DWJ::demo_global_int_b); NSLog(@"%d", DWJ::demo_global_func()); return 0; }
 
警告如下
ld: warning: duplicate symbol 'DWJ::demo_global_func()' in: /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libblib.a(blib.o) /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libalib.a(alib.o) ld: warning: duplicate symbol 'DWJ::demo_global_int' in: /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libblib.a(blib.o) /Users/xxx/Library/Developer/Xcode/DerivedData/demo-duplicate-symbol-gbopvgpqkxegdrbttwbnetsvfncd/Build/Products/Debug-iphonesimulator/libalib.a(alib.o)
 
这还只是静态库 duplicate symbol 的小小探索,如果是动态库,也是有类似问题的,有空再补上。
 
这个故事告诉我们不能忽视两个事情
 
  1. 命名
  1. 警告