strip命令用于去除ELF等二进制文件的调试信息与符号表,默认删符号表和.debug*等节,但保留重定位;strip -g仅删调试信息,--strip-all会删除main等关键符号致程序崩溃;opt -strip-debug仅适用于LLVM IR阶段,不可用于已编译的目标文件。

直接用 strip 或 opt -strip-debug 就能移除,但要注意顺序和目标格式 —— 用错时机或工具链不匹配会导致符号残留或崩溃。
strip 命令:最常用,但只处理已链接的二进制
适用于 ELF(Linux)、Mach-O(macOS)等最终可执行文件或共享库:
-
strip默认剥离符号表、调试节(.debug_*、.line、.stab*),但不碰重定位或动态符号表 - 若需保留动态符号(如供 dlopen 使用),加
-g只删调试信息:strip -g binary - 若用
strip --strip-all,会连main符号都删掉,导致无法运行(尤其无-pie的静态链接程序) - 注意:对未 strip 的 .so 文件,Android NDK 的
ndk-build或 CMake 默认会在install阶段自动 strip;但若你手动构建了RelWithDebInfo类型的 so,必须显式调用strip才真正清除
opt -strip-debug:专用于 LLVM IR 阶段清理
在 bitcode 或 .ll 文件上操作,适合编译流水线中提前剥离,避免调试信息污染后续优化:
-
opt -strip-debug input.bc -o output.bc会移除所有!dbg元数据、!llvm.dbg.*命名元数据,以及关联的DIGlobalVariable等调试记录 - 它不修改指令本身,也不影响控制流或常量池,安全但仅限 IR 层 —— 若你已生成机器码(.o/.so),
opt就无效了 - 常见误用:在
clang -O2 -c生成 .o 后再跑opt,失败,因为 .o 不是 LLVM bitcode 格式 - 正确位置:插在
clang -emit-llvm输出 .bc 后、llc或lld之前
Clang 编译时控制:源头禁用,一劳永逸
比事后清理更干净,适合 CI/CD 或 release 构建脚本:
- 完全不生成调试信息:
clang -g0 source.c(注意是-g0,不是-g后跟数字) - 若用 CMake,设
CMAKE_BUILD_TYPE=Release,默认禁用-g;但若手动加了add_compile_options(-g),仍会生效 —— 要检查整个构建树 - Android NDK 中,
APP_STRIP_MODE=none会跳过自动 strip,此时必须靠-g0或opt补救 - 特别注意:
-gline-tables-only会保留行号但删函数名/变量名,看似“轻量”,但对逆向者已足够定位逻辑 —— 发布版应避免
dsymutil 和 .dSYM:macOS 特有,别漏掉符号束
macOS/iOS 上,调试信息常被抽成独立的 .dSYM 文件束,主二进制里只剩 UUID 引用:
-
dsymutil -s binary可直接剥离主二进制中的 UUID 引用,让系统无法关联回调试符号 - 但若你只删了
.dSYM目录而没运行dsymutil -s,崩溃日志仍可能显示模糊地址(因 UUID 还在) - CI 打包时,务必确认
.dSYM是否随安装包分发 —— 苹果 App Store 会拒绝含完整.dSYM的提交
真正干净的发布版,得同时满足:IR 层无 !dbg、对象文件无调试节、链接后二进制被 strip -g 处理、macOS 下 UUID 引用被清除。任何一环遗漏,都可能让 llvm-addr2line 或反汇编器还原出源码路径。

















