为何不默认使用-no-plt替代-z now?存在哪些技术权衡?
为什么不默认使用
-no-plt?技术权衡解析 你的疑问切中了动态链接机制里的核心矛盾——当我们已经通过-z relro+-z now将GOT提前填充完成时,PLT的延迟绑定设计确实显得冗余。但编译器或发行版没有默认启用-no-plt,核心是出于兼容性、性能场景适配、工具链生态这几方面的现实权衡:
1. 旧环境的兼容性要求
-no-plt是GCC 8及以后才支持的特性,同时依赖新版本glibc(动态链接器ld-linux.so)对GOT直接跳转符号的支持。对于需要兼容旧系统(如嵌入式Linux、CentOS 7这类长期维护的老发行版)的场景,-no-plt会导致程序无法运行;而PLT机制已经存在数十年,几乎所有旧的动态链接器和工具链都能完美适配。
2. 启动速度与运行开销的平衡
- 当不启用
-z now时,PLT的延迟绑定能显著降低大型程序的启动时间:程序启动时不需要一次性解析所有动态库符号,只有当函数第一次被调用时才触发符号解析并填充GOT。这种懒加载策略对依赖数十个动态库的大型应用(如桌面软件、分布式服务)至关重要。 - 而
-no-plt虽然去掉了PLT的跳转开销,但在默认延迟绑定模式下,第一次调用函数的解析逻辑本质和PLT类似——只是把跳转逻辑从PLT移到了GOT条目里。对于不需要-z now的场景,PLT经过多年优化(比如动态链接器对PLT的缓存处理),反而比-no-plt的实现更稳定。
3. 调试与性能分析工具的兼容性
大量调试工具(如GDB)、性能分析工具(如perf)都是基于PLT结构设计的:它们依赖PLT的跳转指令跟踪函数调用、统计调用次数。虽然新版本工具已支持-no-plt程序,但旧版本工具可能无法正确解析-no-plt生成的二进制文件,导致调试或性能分析失效。发行版默认保留PLT,是为了保证工具链生态的广泛兼容性。
4. 完全RELRO场景下的-no-plt优势
你提到的“加载时完全填充GOT”(即-z relro+-z now)场景下,-no-plt确实是更优选择:它去掉了冗余的PLT跳转层,减少了一次指令跳转开销,同时保留了RELRO的安全性。现在很多新发行版的安全编译选项里已经把-no-plt和-z relro+-z now搭配使用,但没有全局默认启用,还是为了兼顾那些不需要完全RELRO的场景。
内容的提问来源于stack exchange,提问作者Ofek Shilon
相关产品推荐
相关产品推荐

