C++ 编译符号导出方法
编译符号导出
编译器符号导出规则
- win (MSVC)默认所有的符号都是隐藏的。
- android、iOS、MacOS(gcc/clang)默认所有的符号都是可见的。4.0以上版本的GCC可以使用 -fvisibility=hidden 设置默认符号隐藏,低版本GCC不支持符号隐藏):。
函数/类定义时导出
- 通常可以在函数定义时,添加属性,显式导出单个函数。
- 文件内函数使用static前缀,避免函数导出。
1 | //************************************************ |
第三方静态库符号导出
当我们编译的动态库有依赖第三方的静态库,且需要导出其函数符号时,需要通过编译选项进行导出符号设置。
Window(dll)
可以使用 #pragma 预处理进行显式导出符号声明。最终在编译链接时,会通过编译选项 /I (Additional include directories) 导出符号。
1 | // example: export c func abase_remoteconfig_GetBool |
注意:C函数导出符号,win32 以下划线开头,win64 不带开头下划线。
macOS/iOS(dylib)
使用编译选项 -exported_symbols_list 指定导出符号列表的文本文件。
从链接到的静态库中排除符号,可以使用链接器选项–exclude-libs lib1,lib2,…或–exclude-libs ALL指定所有静态库。
1 | export.txt 中为导出符号列表 |
XCode 编译配置可以在直接指定 Exported Symbols File。
注意:设置 exported_symbols_list 后,非列表里的符号则都不会再导出。还有个 unexport 的设置,跟 export 不能同时用。
Andoird(so)
- 暂未遇到使用场景,待补充。
符号查看
使用 nm 列出.o .a .so 中的符号信息,包括诸如符号的值,符号类型及符号名称等。所谓符号,通常指定义出的函数,全局变量等等。
1 | nm [option(s)] [file(s)] |
有用的 options:
- -A 在每个符号信息的前面打印所在对象文件名称;
- -C 输出demangle过了的符号名称(--demangle);
- -D 打印动态符号;
- -l 使用对象文件中的调试信息打印出所在源文件及行号;
- -n 按照地址/符号值来排序;
- -u 打印出那些未定义的符号;
- -m 按照 darwin 格式输出(--format=darwin);
常见的 符号类型:
- A 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
- B 该符号放在非初始化数据段段(bss)中,通常是那些未初始化的全局变量;
- D 该符号放在普通的数据段(data section)中,通常是那些已经初始化的全局变量;
- T 该符号放在代码段(text section)中,通常是那些全局非静态函数;
- U 该符号未定义过,需要自其他对象文件中链接进来;
- S 符号位于非初始化数据区,用于small object。
- W 未明确指定的弱链接符号;同链接的其他对象文件中有它的定义就用上,否则就用一个系统特别指定的默认值。
- N 该符号是一个debugging符号。
- ? 该符号类型没有定义
注意事项:
- -C 总是适用于c++编译出来的对象文件。还记得c++中有重载么?为了区分重载函数,c++编译器会将函数返回值/参数等信息-附加到函数名称中去形成一个mangle过的符号,那用这个选项列出符号的时候,做一个逆操作,输出那些原始的、我们可理解的符号名称。
- 使用 -l 时,必须保证你的对象文件中带有符号调式信息,这一般要求你在编译的时候指定一个 -g 选项,见 Linux:Gcc。
- 使用nm前,最好先用Linux:File查看对象文件所属处理器架构,然后再用相应交叉版本的nm工具。
强弱符号
强符号、弱符号
对于 C/C++ 语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号(Clang 下无效,需要配合扩展显式指定符号的强弱类型)。
强符号:函数、已经初始化的全局变量、类外面定义的成员函数、模板函数的特化版本;
弱符号:未初始化的全局变量、类中定义的成员函数、模板函数、模板类中的成员函数。
使用规则:
- 不允许强符号被多次定义(即不同目标文件中不能有同名的强符号);
- 如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,则链接时链接器会选择使用强符号;
- 如果一个符号在所有目标文件中都是弱符号,则链接器通常会选择其中类型占用空间最大(Clang 下直接使用当前编译单元的符号值)的一个。
其他
- -dead_strip项是删除多余的库符号
- -all_load让链接器把所有找到的目标文件都加载到可执行文件中
- -force_load所做的事情跟-all_load其实是一样的,但是-force_load需要指定要进行全部加载的库文件的路径