目录
一、前言——objdump 工具简介
1.1 什么是 objdump
objdump 是 GNU Binutils 中的 目标文件分析与反汇编工具,可读 ELF 可执行文件、.o、.a、.so、.ko 等,输出 段头、符号、反汇编、重定位、源码混排 等信息。
一句话定位:
objdump 是二进制的 「解剖刀」 —— 不用跑起来,就能看清段怎么排、函数汇编长什么样、调用落在哪。
1.2 与相关工具对比
| 工具 | 侧重 | 典型场景 |
|---|---|---|
| objdump | 反汇编 + 段/重定位总览 | 看汇编、对地址、查调用 |
| nm | 符号表一览 | 「有没有这个符号」 |
| readelf | ELF 结构字段 | 段表、动态节、解释器 |
| gdb | 运行时调试 | 断点、变量、core |
| addr2line | 地址→源码行 | 崩溃地址反查 |
| perf annotate | 热点旁注汇编 | 性能热点指令 |
链接报错 / 行为怪异 / 崩溃地址
├─ nm / readelf → 符号、段、依赖在不在
├─ objdump -d/-S → 函数汇编与调用关系
├─ addr2line → 地址对应哪一行
└─ gdb → 运行时验证
二、使用场景——何时使用 objdump
1、确认编译器到底生成了什么指令
优化开没开、inline 有没有真正内联、分支是否被优化掉:
objdump -d ./app | less
objdump -d ./app | grep -A40 '<hot_func>:'
2、崩溃 / dmesg 只有地址时对照汇编
objdump -d ./app | grep -n '1234:' # 粗查
# 更好:gdb list *addr 或 addr2line;objdump 用于看附近指令序列
3、源码 + 汇编对照(需 -g)
gcc -g -O2 -c foo.c
objdump -S -d foo.o
# 或
objdump -S -d ./app | less
4、查看段布局与文件头
objdump -h ./app # section headers
objdump -f ./app # file header 摘要
objdump -p ./app # private headers(程序头等,视格式)
5、静态库 / 目标文件里「谁调用了谁」
objdump -d libfoo.a
objdump -r foo.o # 重定位项
objdump -T ./lib.so # 动态符号(类似动态 nm)
6、与 nm 分工:nm 找符号,objdump 看身体
nm ./app | grep my_func
objdump -d ./app | sed -n '/<my_func>:/,/^$/p'
7、内核模块 / 固件粗看
objdump -d mymod.ko | less
objdump -h mymod.ko
注意:.ko 未加载时许多地址为 0,重定位后才是运行时地址。
三、核心原理——工作原理深度解析
3.1 读的是什么
objdump 解析目标文件格式(Linux 上主要是 ELF):
ELF File
├─ ELF Header → -f
├─ Program Headers → -p(加载视图)
├─ Section Headers → -h(链接视图:.text/.data/.rodata…)
├─ .symtab / .dynsym → -t / -T
├─ .rela* → -r / -R
└─ .text 字节流 → -d / -D 反汇编成指令
反汇编时:按机器架构(x86_64、ARM…)把 机器码 译成 助记符;若有调试信息,-S 可把源码行插入汇编旁。
3.2 -d 与 -D 的区别
| 选项 | 行为 |
|---|---|
|
| 只反汇编 期望含代码的段(通常 |
|
| 试图反汇编 更多段(含数据段,结果可能「假指令」) |
日常看函数用 -d;怀疑代码落在非常规段再用 -D。
3.3 地址:VMA vs 文件偏移
objdump -h ./app
# 列: VMA / LMA / File off / Algn
| 概念 | 含义 |
|---|---|
| VMA | 加载后虚拟地址(反汇编左边一列通常是这个) |
| File off | 在文件中的偏移 |
| PIE/ASLR | 运行时基址会变;objdump 显示的是 链接时/文件中的地址 |
因此:线上 dmesg 的 ip 要先减掉加载基址,再和 objdump -d 里的地址对齐;或直接用 gdb/addr2line 更省事。
3.4 与「可执行视图」
objdump -d 展示的是 静态镜像;动态链接的外部调用常表现为:
call 401030 <printf@plt>
真正跳进 libc 要经 PLT/GOT,运行时由动态链接器填好。objdump 看到的是 PLT 桩,不是最终 libc 指令流。
3.5 反汇编质量依赖什么
-
正确的 架构(
--architecture/ 交叉 objdump) -
是否有 符号(strip 后函数名变
addr) -
是否有 调试信息(
-S才好看) -
编译优化等级(
-O0与-O2判若两人)
四、命令参数——常用选项详解
4.1 高频选项
| 选项 | 含义 |
|---|---|
|
| 反汇编代码段 |
|
| 反汇编所有相关段 |
|
| 反汇编并混排源码(需 |
|
| section headers |
|
| 文件头摘要 |
|
| 几乎所有头信息 |
|
| 符号表(类似 nm) |
|
| 动态符号表 |
|
| 重定位信息 |
|
| 动态重定位 |
|
| 以十六进制转储各段内容 |
|
| 只处理指定段 |
|
| C++ 名字还原 |
|
| 行号信息(与源码相关) |
|
| x86 用 Intel 语法(默认 AT&T) |
|
| AT&T 语法 |
|
| 限制反汇编地址范围 |
|
| 宽行,少折行 |
4.2 常用组合
objdump -d ./app # 基础反汇编
objdump -dC ./app # + C++ demangle
objdump -S -d ./app # 源码交叉
objdump -M intel -d ./app # Intel 语法
objdump -d --start-address=0x401000 --stop-address=0x401200 ./app
objdump -h -j .text -j .data ./app
objdump -s -j .rodata ./app | head
objdump -t ./app | grep ' F .text'
4.3 交叉与指定机器
aarch64-linux-gnu-objdump -d firmware.elf
objdump -m aarch64 -d something.bin # 原始二进制需指定 arch(慎用)
4.4 输出怎么读(x86_64 AT&T 示例)
0000000000401136 <main>:
401136: 55 push %rbp
401137: 48 89 e5 mov %rsp,%rbp
40113a: e8 f1 fe ff ff call 401030 <puts@plt>
40113f: b8 00 00 00 00 mov $0x0,%eax
401144: 5d pop %rbp
401145: c3 ret
| 列 | 含义 |
|---|---|
| 左侧地址 | 指令 VMA |
| 中间机器码 | 原始字节 |
| 右侧助记符 | 反汇编文本 |
|
| 符号入口 |
五、使用实战——实际案例分析
示例:objdump_demo.c
#include <stdio.h>
volatile int g_sink;
/* 简单算术:看寄存器传参与 add */
__attribute__((noinline)) int add_ints(int a, int b)
{
return a + b;
}
/* 故意保留循环体,便于 O0/O2 对比 */
__attribute__((noinline)) int hot_loop(int n)
{
int i, s = 0;
for (i = 0; i < n; i++)
s += i * 3 + 1;
return s;
}
__attribute__((noinline)) void leaf(int x)
{
g_sink = x;
}
__attribute__((noinline)) void call_chain(int x)
{
leaf(add_ints(x, 1));
}
static const char k_msg[] = "objdump_demo: hello rodata";
int main(void)
{
int r = hot_loop(100);
call_chain(r);
printf("%s sink=%d sum=%d\n", k_msg, g_sink, add_ints(2, 3));
return 0;
}
Makefile
CC ?= gcc
CFLAGS ?= -g -O0 -Wall -Wextra -fno-omit-frame-pointer
TARGET := objdump_demo
.PHONY: all clean demo disasm intel source rodata reloc strip-cmp help
all: $(TARGET)
$(TARGET): objdump_demo.c
$(CC) $(CFLAGS) -o $@ $<
objdump_demo.o: objdump_demo.c
$(CC) $(CFLAGS) -c -o $@ $<
disasm: $(TARGET)
objdump -d $(TARGET) | sed -n '/<add_ints>:/,/^$$/p'
intel: $(TARGET)
objdump -M intel -d $(TARGET) | sed -n '/<add_ints>:/,/^$$/p'
source: $(TARGET)
objdump -S -d $(TARGET) | sed -n '/<call_chain>:/,/^$$/p'
rodata: $(TARGET)
objdump -s -j .rodata $(TARGET)
reloc: objdump_demo.o
objdump -r objdump_demo.o
@echo "----"
objdump -d objdump_demo.o | head -50
strip-cmp: $(TARGET)
cp $(TARGET) $(TARGET).nostrip
strip -o $(TARGET).stripped $(TARGET).nostrip
@echo "=== with symbols ==="
objdump -d $(TARGET).nostrip | grep -E '<add_ints>:' || true
@echo "=== stripped (name may disappear) ==="
objdump -d $(TARGET).stripped | grep -E '<add_ints>:' || echo "(no add_ints symbol label)"
demo: all
@echo "objdump -d ./$(TARGET) | less"
@echo "make disasm | intel | source | rodata | reloc | strip-cmp"
clean:
$(RM) $(TARGET) $(TARGET).o $(TARGET).nostrip $(TARGET).stripped objdump_demo.o
help: demo
5.1 案例一:看某个函数的汇编
objdump -d ./objdump_demo | sed -n '/<add_ints>:/,/^$/p'
# 或
objdump -d ./objdump_demo | awk '/<add_ints>:/{p=1} p; /^$/{if(p&&++n>0) exit}'
期望: 看到参数传递(System V AMD64:edi/esi 等)、add、ret。

5.2 案例二:-O0 vs -O2 对比
make clean && make CFLAGS='-g -O0'
objdump -d ./objdump_demo | sed -n '/<hot_loop>:/,/^$/p' > /tmp/o0.txt
make clean && make CFLAGS='-g -O2'
objdump -d ./objdump_demo | sed -n '/<hot_loop>:/,/^$/p' > /tmp/o2.txt
diff -u /tmp/o0.txt /tmp/o2.txt | less
期望: -O2 循环更紧、可能少存栈、指令更短或向量化痕迹。

5.3 案例三:源码混排 -S
make CFLAGS='-g -O1'
objdump -S -d ./objdump_demo | sed -n '/<call_chain>:/,/^$/p'
期望: C 源码行与汇编交错,便于对照「这一行变成了哪些指令」。

5.4 案例四:PLT 与外部调用
objdump -d ./objdump_demo | grep -E 'printf@plt|puts@plt|call'
objdump -R ./objdump_demo | head # 动态重定位
期望: 对 libc 的调用经 @plt;-R 可见 JUMP_SLOT 等。


5.5 案例五:只看 .rodata 字符串
objdump -s -j .rodata ./objdump_demo
# 或
objdump -s ./objdump_demo | sed -n '/Contents of section \.rodata/,/^Contents/p'
期望: 可见字符串字面量十六进制/ASCII。

5.6 案例六:Intel 语法与地址窗
objdump -M intel -d ./objdump_demo | sed -n '/<add_ints>:/,/^$/p'
ADDR=$(nm ./objdump_demo | awk '/ add_ints$/{print $1}')
objdump -d --start-address=0x$ADDR ./objdump_demo | head -20

5.7 案例七:目标文件与重定位
make objdump_demo.o
objdump -r objdump_demo.o
objdump -d objdump_demo.o | head -40
期望: .o 中对全局/外部符号的 R_X86_64_* 重定位;链接后地址被钉死或改走 PLT。

5.8 案例八:strip 前后对比
cp objdump_demo objdump_demo.nostrip
strip objdump_demo -o objdump_demo.stripped
objdump -d objdump_demo.nostrip | grep '<add_ints>'
objdump -d objdump_demo.stripped | grep '<add_ints>' || echo "symbol name gone"
objdump -t objdump_demo.stripped | head
期望: strip 后反汇编还在,但函数名符号大量消失,只剩地址。
5.9 与 gdb / nm / readelf 联动手势
nm ./app | grep foo # 符号在不在、地址多少
objdump -d ./app | ... # 该地址指令序列
readelf -S ./app # 段权限、对齐
gdb ./app -ex "disassemble foo" -batch # 运行时/带调试的另一种反汇编
六、常见问题——疑难解答
6.1 中文乱码 / 看不懂 AT&T
objdump -M intel -d ./app # 改 Intel 语法
# 或学习 AT&T:src/dst 顺序与 Intel 相反,立即数带 $,寄存器带 %
6.2 -S 没有源码
需编译加 -g,且源文件路径仍可访问;剥离调试信息后无效。
6.3 地址和运行时对不上
PIE + ASLR。用:
gdb -q ./app -ex "start" -ex "info proc mappings" -ex quit
# 或
readelf -h ./app | grep Type # DYN=PIE
objdump 地址是链接视图;运行时基址另算。
6.4 反汇编「数据段」出现奇怪指令
那是 -D 把数据当代码解了。改用 -d,或只 -j .text。
6.5 交叉架构反汇编失败
换对应三元组的 *-objdump,或 -m <arch>。
6.6 很大的文件刷屏
objdump -d ./app --start-address=... --stop-address=...
objdump -d ./app | less
llvm-objdump -d --symbolize-operands # 若更习惯 LLVM 工具
6.7 与 llvm-objdump 的差异
选项大体类似,细节与默认语法可能不同;团队统一一种即可。
七、总结——要点回顾
7.1 三句话
-
objdump 把 ELF 的代码与元数据变成可读的段表和汇编。
-
日常:
objdump -d看函数;-S对照源码;-h/-r/-R看布局与重定位。 -
和 nm/readelf/gdb 分工:nm 找名,objdump 看指令,gdb 看运行时。
7.2 推荐工作流
符号在哪 → nm / objdump -t
函数咋编译 → objdump -d / -S
外部咋调 → 看 @plt,objdump -R
崩溃地址 → 换算后对照 objdump,或直接 gdb/addr2line
优化效果 → 同函数 O0/O2 的 objdump diff
7.3 最小命令清单
objdump -d ./app
objdump -dC ./app | less
objdump -S -d ./app
objdump -M intel -d ./app
objdump -h ./app
objdump -t ./app
objdump -T ./lib.so
objdump -r ./foo.o
objdump -R ./app
objdump -s -j .rodata ./app
objdump -d --start-address=0x... --stop-address=0x... ./app
7.4 调试工具链位置
| 工具 | 回答的问题 |
|---|---|
| objdump | 这段二进制指令/段长什么样? |
| nm | 有哪些符号? |
| readelf | ELF 头/段/动态节细节? |
| addr2line | 地址对应源码行? |
| gdb | 运行时栈与变量? |
| perf annotate | 热点落在哪些指令? |
&spm=1001.2101.3001.5002&articleId=163052730&d=1&t=3&u=f94eb95e853842498b2d639be776f53a)
4115

被折叠的 条评论
为什么被折叠?



