[智能芯片] 可执行文件分析
《智能芯片》课程笔记:可执行文件分析
📋 目录
获取汇编代码与机器码
🔧 objdump 工具简介
| 属性 | 说明 |
|---|---|
| 工具名称 | riscv64-unknown-elf-objdump.exe |
| 核心功能 | 对 ELF 可执行文件进行反汇编,提取汇编代码与机器码 |
| 适用场景 | 分析编译后程序的底层实现、调试、学习指令集 |
📌 基本使用命令
# 1. 直接在终端显示反汇编结果
riscv64-unknown-elf-objdump.exe -S example.elf
# 2. 将结果重定向保存到文件(推荐)
riscv64-unknown-elf-objdump.exe -S example.elf > example.S.txt
💡
-S选项:混合显示源代码、汇编代码和机器码,便于对照理解
📖 反汇编输出格式示例解读

🎨 颜色/内容对照表
| 颜色/位置 | 内容类型 | 说明 |
|---|---|---|
| ⚫ 黑色 | C源代码 | 原始高级语言代码 |
| 🟢 绿色 | 存储地址 | 机器码在内存中的起始地址 |
| 🔴 红色 | 机器码 | 汇编指令对应的16进制机器码 |
| 🔵 蓝色 | 汇编代码 | 编译生成的RISC-V汇编指令 |
| ⚪ 灰色 | 注释 | 编译器添加的辅助信息 |
获取函数与变量的地址
❓ 问题背景
上一节范例程序中定义了:
- 变量:
f,s,t,p,q,a,b - 函数:
main,fun1,fun2
🎯 目标:如何获取这些符号在内存中的具体地址?
🔍 方法一:通过 .map 文件(编译时生成)
📌 前提条件
编译时需添加 -Wl,-Map=example.map 选项:
riscv64-unknown-elf-gcc.exe -march=rv32i -mabi=ilp32 -Wall -O1 -g -nostartfiles -T linker_script_onestep.ld -Wl,-Map=example.map -o example.elf startup.s fun.c main.c
📄 example.map 文件内容截取
以上一节生成的 example.map 为例。
.text 0x000000000000004c 0xdc
*(.text)
.text 0x000000000000004c 0x0 C:\Users\*****\AppData\Local\Temp\ccXSS4dc.o
.text 0x000000000000004c 0x64 C:\Users\Lenovo\AppData\Local\Temp\ccNYCzQf.o
0x000000000000004c fun1
0x000000000000006c fun2
.text 0x00000000000000b0 0x78 C:\Users\*****\AppData\Local\Temp\ccycWyFb.o
0x00000000000000b0 main
0x0000000000000128 . = ALIGN (0x4)
.data 0x0000000000000400 0xc load address 0x0000000000000128
0x0000000000000400 PROVIDE (_data_start = .)
*(.data)
.data 0x0000000000000400 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccXSS4dc.o
.data 0x0000000000000400 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccNYCzQf.o
.data 0x0000000000000400 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccycWyFb.o
*(.sdata)
.sdata 0x0000000000000400 0xc C:\Users\Lenovo\AppData\Local\Temp\ccycWyFb.o
0x0000000000000400 a
0x0000000000000404 p
0x0000000000000406 s
0x0000000000000408 f
0x000000000000040c . = ALIGN (0x4)
0x000000000000040c PROVIDE (_data_end = .)
0x000000000000040c PROVIDE (_bss_start = .)
.bss 0x000000000000040c 0x8
*(.bss)
.bss 0x000000000000040c 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccXSS4dc.o
.bss 0x000000000000040c 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccNYCzQf.o
.bss 0x000000000000040c 0x0 C:\Users\Lenovo\AppData\Local\Temp\ccycWyFb.o
*(.sbss*)
.sbss 0x000000000000040c 0x4 C:\Users\Lenovo\AppData\Local\Temp\ccNYCzQf.o
0x000000000000040c b
.sbss 0x0000000000000410 0x4 C:\Users\Lenovo\AppData\Local\Temp\ccycWyFb.o
0x0000000000000410 q
0x0000000000000412 t
*(COMMON)
0x0000000000000414 . = ALIGN (0x4)
0x0000000000000414 PROVIDE (_bss_end = .)
📊 提取的地址信息
| 类型 | 符号 | 地址(16进制) |
|---|---|---|
| 🔹 函数 | fun1 |
0x4c |
| 🔹 函数 | fun2 |
0x6c |
| 🔹 函数 | main |
0xb0 |
| 🔸 变量 | a |
0x400 |
| 🔸 变量 | p |
0x404 |
| 🔸 变量 | s |
0x406 |
| 🔸 变量 | f |
0x408 |
| 🔸 变量 | b |
0x40c |
| 🔸 变量 | q |
0x410 |
| 🔸 变量 | t |
0x412 |
🔍 方法二:使用 nm 工具(分析已编译文件)
📌 使用命令
riscv64-unknown-elf-nm.exe example.elf
📄 输出示例
00000400 A __RAM_BASE
00000400 A __RAM_SIZE
00000000 A __ROM_BASE
00000400 A __ROM_SIZE
00000200 A __STACK_SIZE
00000414 B _bss_end
0000040c D _bss_start
0000040c D _data_end
00000128 T _data_lma
00000400 D _data_start
00000800 B _sp
00000000 T _start
00000400 D a
0000040c B b
00000408 D f
0000004c T fun1
0000006c T fun2
000000b0 T main
00000404 D p
00000410 B q
00000406 D s
00000412 B t
🔑 符号类型说明(第2列字母)
| 字母 | 含义 | 对应段 |
|---|---|---|
T / t |
文本段(Text)函数 | .text,代码区 |
D / d |
数据段(Data)已初始化全局/静态变量 | .data |
B / b |
BSS段未初始化全局/静态变量 | .bss |
A |
绝对地址符号,不可重定位 | - |
U |
未定义符号(需链接时解析) | - |
💡 大写表示全局符号(外部可见),小写表示局部符号(文件内可见)
反汇编代码实例解析 🧪
📝 反汇编代码片段
080000a8<fun1>:
int fun1(int x, int y, float z)
{
char* p = (char*)&a;
*(p+6) = 0xA9;
80000a8: 200007b7 lui a5,0x20000
80000ac: 00478793 addi a5,a5,4 # a5 = &a
80000b0: fa900713 li a4,-87 # a4 = 0xA9
80000b4: 00e78323 sb a4,6(a5) # *(p+6) = 0xA9
*(p+7) = 0x42;
80000b8: 04200713 li a4,66 # a4 = 0x42
80000bc: 00e783a3 sb a4,7(a5) # *(p+7) = 0x42
return y;
80000c0: 00058513 mv a0,a1 # 返回值 = y
80000c4: 00008067 ret # 函数返回
}
❓ 问题与解答
| 问题 | 解答 | 分析过程 |
|---|---|---|
| (1) fun1函数地址? | 0x80000a8 |
函数标签 <fun1> 后的起始地址 |
| (2) 机器码占用字节数? | 32 Bytes |
0x80000c7 - 0x80000a8 + 1 = 31+1 = 32 |
| (3) 地址 0x80000ae 存储值? | 0x47 |
机器码 00478793 小端存储:[ac]=93, [ad]=87, [ae]=47, [af]=00 |
(4) *(p+7)=0x42 实现方式? |
2条汇编指令,8字节机器码 | li a4,66 (4B) + sb a4,7(a5) (4B) |
🔍 小端模式(Little-Endian)存储示意
机器码:00478793(32位,16进制)
内存地址(低→高): 80000ac 80000ad 80000ae 80000af
存储内容(16进制): 93 87 47 00
↑低字节 ↑高字节
💡 RISC-V 采用小端模式:低字节存低地址,高字节存高地址
拓展:分析工具使用说明 🛠️
📦 Binutils 工具集概览
| 工具 | 功能 | 常用场景 |
|---|---|---|
objdump |
反汇编、查看段信息 | 分析机器码与汇编对应关系 |
nm |
列出符号表 | 查找函数/变量地址 |
size |
显示各段大小 | 分析程序内存占用 |
objcopy |
格式转换、提取段 | 生成bin/hex文件用于烧录 |
readelf |
解析ELF文件结构 | 深入理解可执行文件格式 |
🔎 查看工具帮助信息
# 通用方法:添加 --help 参数
riscv64-unknown-elf-objdump.exe --help
riscv64-unknown-elf-nm.exe --help
# 查看特定选项说明
riscv64-unknown-elf-objdump.exe --help | grep -i "disassemble"
🎯 常用组合命令示例
# 1. 反汇编并过滤特定函数
riscv64-unknown-elf-objdump -S example.elf | grep -A 20 "<fun1>:"
# 2. 查看所有全局符号地址
riscv64-unknown-elf-nm -g example.elf
# 3. 查看程序各段大小
riscv64-unknown-elf-size example.elf
# 输出:text data bss dec hex filename
# 1234 567 89 1890 762 example.elf
# 4. 提取纯机器码(二进制格式)
riscv64-unknown-elf-objcopy -O binary example.elf example.bin
笔记整理基于《智能芯片》课程课件,适用于《智能芯片》课程学习参考 🚀
📚 主讲人:柳星
📧 邮箱:liu.xing@whut.edu.cn
🎯 课程核心:理解可执行文件的内部结构与分析方法

本文介绍了可执行文件的分析方法。通过`objdump`和`nm`等工具,详细讲解了如何从ELF文件中提取汇编代码、机器码及函数变量的内存地址,并结合.map文件和实例解析,帮助理解程序的存储布局与底层执行机制。
浙公网安备 33010602011771号