strip 是一个在 Linux 和其他类 Unix 系统上非常常用的命令行工具,它的核心功能是从可执行文件或目标文件中移除符号表和调试信息。

strip 命令的主要用途
为什么需要 strip 命令呢?主要有以下几个原因:
-
减小文件大小:
- 编译器在编译程序时,会生成包含大量调试信息的符号表(如变量名、函数名、行号等),这些信息对于开发者调试程序至关重要,但对于最终用户来说毫无用处。
strip可以移除这些冗余信息,从而显著减小可执行文件或共享库的体积,这对于需要分发软件、节省磁盘空间或减少网络传输开销的场景非常有帮助。
-
提高加载速度和运行时性能:
- 当操作系统加载一个可执行文件时,如果它包含符号表,加载器需要处理这些信息,这会增加启动时间。
- 移除符号表后,加载过程更快,程序的启动速度也会有所提升。
-
增加一定的安全性(反逆向工程):
(图片来源网络,侵删)- 符号表包含了程序的结构信息,如函数名和全局变量名,这使得逆向工程师更容易理解程序的工作原理和逻辑。
strip可以移除这些“线索”,增加逆向工程的难度,虽然这不能完全阻止逆向,但确实提高了门槛。
strip 的工作原理
strip 并不是“加密”或“混淆”代码,它只是简单地删除或剥离特定的数据段:
.dynsym/.symtab:符号表,包含了函数和变量的名称和地址。.debug_info,.debug_line,.debug_abbrev等:GNU 调试信息,由gcc -g选项生成,用于gdb等调试器。.comment:编译器版本信息等注释。
strip 会保留代码本身(.text 段)和程序运行所必需的动态链接信息(.dynstr, .dynstr 等),只移除那些对最终运行非必需的辅助信息。
常用选项
strip 命令提供了一些有用的选项来控制其行为。
基本用法
strip [选项] 文件名...
常用选项详解
| 选项 | 全称 | 描述 |
|---|---|---|
-s |
--strip-all |
最常用的选项,剥离所有符号表和调试信息,适用于大多数可执行文件和共享库。 |
-g |
--strip-debug |
只剥离调试信息(.debug_* 段),但保留符号表,这对于共享库(.so 文件)很重要,因为程序运行时可能需要这些符号来动态链接。 |
--strip-unneeded |
剥离对文件链接和执行非必需的符号,通常与 -g 结合使用,效果更好。 |
|
-K |
--keep-symbol= |
保留指定的符号。strip -K main my_program 会保留名为 main 的符号,即使它可能被其他选项标记为可剥离。 |
-N |
--keep-section= |
保留指定的节(section)。strip -N .comment 会保留 .comment 节,剥离其他所有调试信息。 |
-R |
--remove-section= |
移除指定的节。strip -R .comment 会移除 .comment 节。 |
-o |
--output= |
指定输出文件名,而不是直接覆盖原文件。强烈建议使用此选项进行测试,避免意外损坏原始文件。 |
-v |
--verbose |
显示详细的处理信息。 |
--relocatable |
创建一个“可重定位”的文件,可以与其他目标文件链接,这个选项比较特殊,不常用于最终分发。 | |
--help |
显示帮助信息。 | |
--version |
显示 strip 程序的版本信息。 |
实际示例
假设我们有一个用 C 语言编写的简单程序 hello.c。

步骤 1: 编译程序(包含调试信息)
# -g 选项生成调试信息 # -o 指定输出文件名 gcc -g -o hello hello.c # 查看文件大小 ls -lh hello # 输出可能类似:-rwxr-xr-x 1 user user 16K Jun 20 10:30 hello
可以看到,hello 文件有 16KB,这包含了代码和调试信息。
步骤 2: 使用 strip 剥离文件
场景 A:剥离所有信息(适用于最终可执行文件)
# 使用 -s 或 --strip-all 剥离所有非必要信息 strip -s hello # 再次查看文件大小 ls -lh hello # 输出可能类似:-rwxr-xr-x 1 user user 8.5K Jun 20 10:31 hello
可以看到,文件大小从 16KB 减小到了 8.5KB,减少了近一半!你将无法使用 gdb hello 来调试它。
场景 B:只剥离调试信息(适用于共享库 .so)
假设我们编译了一个共享库 libmylib.so。
# 错误的做法:会破坏库的功能 strip -s libmylib.so # 正确的做法:只剥离调试信息,保留符号表 strip -g libmylib.so
为什么不能对 .so 文件使用 -s?因为动态链接器在程序运行时需要库中的符号表(printf, malloc 等函数名)来解析和绑定函数调用,如果剥离了,程序在运行时会找不到这些符号,导致 segmentation fault 等错误。
场景 C:安全地测试 strip(使用 -o 选项)
这是一个好习惯,可以避免意外损坏你的原始文件。
strip -s -o hello_stripped hello # 现在你有两个文件 ls -lh hello* # hello 16K (原始文件,未改变) # hello_stripped 8.5K (剥离后的新文件) # 确认剥离后的文件可以正常运行 ./hello_stripped # 输出: Hello, World!
场景 D:保留特定符号
假设你的程序有一个导出的 API 函数 my_api_function,你希望即使剥离后,这个符号依然存在(供插件系统使用)。
strip -s -K my_api_function -o hello_api hello
重要注意事项
-
不要轻易剥离库文件(
.so):- 对于共享库(
.so文件),绝对不要使用strip -s,应该使用strip -g来只移除调试信息。 - 对于静态库(
.a文件),可以使用strip -s,但需要先将其转换为可被strip处理的格式(使用ar x解压,然后对每个.o文件strip,最后重新打包)。
- 对于共享库(
-
保留原始文件:
strip是一个破坏性操作,一旦剥离,调试信息就无法恢复。- 在发布软件前,最好保留一份包含完整调试信息的原始版本,以便在出现问题时进行调试。
- 使用
-o选项创建新文件是一个非常好的实践。
-
与构建系统集成:
- 在大型项目中,通常不会手动
strip文件,而是在构建系统(如Makefile,CMake)的发布或安装步骤中自动执行。 - 在 CMake 中,你可以使用
strip命令作为POST_BUILD步骤。
- 在大型项目中,通常不会手动
| 特性 | 描述 |
|---|---|
| 核心功能 | 从二进制文件中移除符号表和调试信息。 |
| 主要优点 | 减小文件大小、提高加载速度、增加逆向难度。 |
| 关键选项 | -s / --strip-all (剥离所有), -g / --strip-debug (只剥调试信息)。 |
| 最佳实践 | 对最终可执行文件使用 -s;对共享库 .so 使用 -g;始终使用 -o 选项进行测试,保留原始文件。 |
strip 是 Linux 系统管理员和开发者工具箱中的一个基础而强大的工具,掌握它能帮助你更好地管理和分发软件。
