linkage 决定符号能否被其他模块看到并参与链接,visibility 控制该符号是否出现在动态符号表里、能否被外部动态引用;两者作用域不同,且在 elf 和 darwin 平台行为不一致。

linkage 决定符号能否被其他模块看到并参与链接,visibility 控制该符号是否出现在动态符号表里、能否被外部动态引用——两者作用域不同,且在 ELF 和 Darwin 平台行为不一致。
linkage 类型决定符号能否被链接器合并或外部引用
linkage 是 IR 层级的链接语义,直接影响链接器是否允许跨 module 合并定义、是否允许外部 module 通过符号名引用它。比如 external 表示该符号必须由其他 module 提供;internal 表示只在当前 module 内可见,链接器会直接丢弃其符号名;linkonce_odr 常用于 C++ inline 函数,允许多个定义但必须一致,链接器任选其一。
-
private:连本 module 的其他函数都不能引用(比如@.str),不会进入符号表 -
available_externally:本 module 不生成代码,仅提供定义供其他 module 内联,自身不保留实体 - 错误现象:把本该是
external的全局变量写成internal,会导致链接时报undefined reference to 'xxx'
visibility 控制符号是否暴露给动态链接器
visibility 是 ELF/Darwin 链接产物层面的属性,只影响 .so/.dylib 的符号导出行为,对静态链接无实质影响。它不改变 linkage,但叠加后限制更严。例如 default visibility + external linkage 表示该符号既可被静态链接,也可被 dlopen 动态加载时解析;而 hidden visibility 即使是 external linkage,也会从动态符号表中剔除。
-
hidden:符号不进.dynsym,其他 shared object 无法dlsym或直接调用 -
protected:进.dynsym,但本 shared object 内部调用不走 PLT,且禁止其他模块覆盖 - 常见误用:在 Linux 上为避免符号冲突加了
hidden,结果 Python extension 用cffi加载时找不到函数 —— 因为cffi依赖动态符号解析
linkage 和 visibility 组合的实际效果容易被平台混淆
同一组 linkage + visibility 在不同平台表现不同。比如 default visibility 在 ELF 下允许符号被动态覆盖,在 Darwin 下只是“可被外部看到”,但 Darwin 没有符号覆盖机制;hidden 在两者下都禁用动态引用,但 Darwin 的 Mach-O 中对应的是 private_extern,语义略有差异。
- Clang 编译时加
-fvisibility=hidden会默认给所有external符号设hiddenvisibility,但不会动 linkage - IR 中显式写
hiddenvisibility 时,必须确保 linkage 至少是external或weak,否则 visibility 无意义(internal符号本来就不会导出) - 调试技巧:用
readelf -Ws xxx.so | grep your_symbol看 symbol 的 BIND(LOCAL/ GLOBAL)和 TYPE(NOTYPE / FUNC / OBJECT),再结合objdump -T看是否在动态符号表出现
真正容易被忽略的是:linkage 影响编译期和链接期行为,visibility 影响运行期动态链接行为;两者不在同一抽象层,不能互相替代,也不能靠猜 —— 必须根据你最终要生成的是静态库、可执行文件还是 shared library,分别查目标平台的 ABI 文档确认组合语义。











