菜鸟科技网

Linux strip命令如何精简可执行文件大小?

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

Linux strip命令如何精简可执行文件大小?-图1
(图片来源网络,侵删)

strip 命令的主要用途

为什么需要 strip 命令呢?主要有以下几个原因:

  1. 减小文件大小

    • 编译器在编译程序时,会生成包含大量调试信息的符号表(如变量名、函数名、行号等),这些信息对于开发者调试程序至关重要,但对于最终用户来说毫无用处。
    • strip 可以移除这些冗余信息,从而显著减小可执行文件或共享库的体积,这对于需要分发软件、节省磁盘空间或减少网络传输开销的场景非常有帮助。
  2. 提高加载速度和运行时性能

    • 当操作系统加载一个可执行文件时,如果它包含符号表,加载器需要处理这些信息,这会增加启动时间。
    • 移除符号表后,加载过程更快,程序的启动速度也会有所提升。
  3. 增加一定的安全性(反逆向工程)

    Linux strip命令如何精简可执行文件大小?-图2
    (图片来源网络,侵删)
    • 符号表包含了程序的结构信息,如函数名和全局变量名,这使得逆向工程师更容易理解程序的工作原理和逻辑。
    • 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

Linux strip命令如何精简可执行文件大小?-图3
(图片来源网络,侵删)

步骤 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

重要注意事项

  1. 不要轻易剥离库文件(.so

    • 对于共享库(.so 文件),绝对不要使用 strip -s,应该使用 strip -g 来只移除调试信息。
    • 对于静态库(.a 文件),可以使用 strip -s,但需要先将其转换为可被 strip 处理的格式(使用 ar x 解压,然后对每个 .o 文件 strip,最后重新打包)。
  2. 保留原始文件

    • strip 是一个破坏性操作,一旦剥离,调试信息就无法恢复。
    • 在发布软件前,最好保留一份包含完整调试信息的原始版本,以便在出现问题时进行调试。
    • 使用 -o 选项创建新文件是一个非常好的实践。
  3. 与构建系统集成

    • 在大型项目中,通常不会手动 strip 文件,而是在构建系统(如 Makefile, CMake)的发布或安装步骤中自动执行。
    • 在 CMake 中,你可以使用 strip 命令作为 POST_BUILD 步骤。
特性 描述
核心功能 从二进制文件中移除符号表和调试信息。
主要优点 减小文件大小、提高加载速度、增加逆向难度。
关键选项 -s / --strip-all (剥离所有), -g / --strip-debug (只剥调试信息)。
最佳实践 对最终可执行文件使用 -s;对共享库 .so 使用 -g;始终使用 -o 选项进行测试,保留原始文件。

strip 是 Linux 系统管理员和开发者工具箱中的一个基础而强大的工具,掌握它能帮助你更好地管理和分发软件。

分享:
扫描分享到社交APP
上一篇
下一篇