目录
1、Binutils工具集
GNU为GCC编译器提供了配套的辅助工具集(Binutils))
http://www.gnu.org/software/binutils/
addr2line
- 将指定地址转换为对应的文件名和行号
- 常用于分析和定位内存访问错误的问题
//func.c
#include <stdio.h>
int* g_pointer;//0
void func()
{
*g_pointer = (int)"D.T.Software";//对0地址处赋值
return;
}
//test.c
#include <stdio.h>
int g_global = 0;
int g_test = 1;
extern int* g_pointer;
extern void func();
int main(int argc, char *argv[])
{
printf("&g_global = %p\n", &g_global);
printf("&g_test = %p\n", &g_test);
printf("&g_pointer = %p\n", &g_pointer);
printf("g_pointer = %p\n", g_pointer);
printf("&func = %p\n", &func);
printf("&main = %p\n", &main);
func();
return 0;
}
如果有50万行代码如何快速定位错误?
addr2line 示例:定位0地址访问
1. 开启core dump选项(程序奔溃的最后一刻记录内存信息,寄存器信息等)
- ulimit -c unlimited
2. 运行程序,并生成崩溃时的 core文件
- 执行导致程序崩溃的测试用例
3. 读取 core文件,获取IP寄存器的值(0x08048000)
- dmesg core
4. 使用addr2line定位代码行
- addr2line 0x08048000 -f -e test.out
几乎所有的调试辅助工具都依赖于目标文件中的调试信息,调试信息的运用能够快速定位问题
strip
- 剔除程序文件中的调试信息,减少目标程序的大小
- 一般在程序发布前都需要将调试信息剔除
- 过多的调试信息可能影响程序的执行效率
ar
- 打包目标文件 ar crs libname.a x.o y.o
- 解压目标文件 ar x libname.a
nm
- 列出目标文件中的标识符(变量名,函数名)
- 输出结果由三部分组成:{地址,段,标识符}
段标识说明
size
- 获取目标文件中的所有段大小
size test.out
strings
- 获取目标文件中的所有字符串常量
strings test.out
objdump
- 反汇编目标文件,查看汇编到源码的映射
objdump -d func.o
objdump -S func.o
- 查看目标文件中的详细段信息
objdump -h test.out
objdump -h的输出说明
2、区分VMA与LMA
VMA(Virtual Memory Address):虚拟内存地址
LMA(Load Memory Address):加载内存地址
在桌面环境下执行可执行文件
- 分配虚存
- 将各个段从可执行文件加载到虚存中
- 执行程序
将test.out对应段拷贝到虚存的段的起始虚地址(段的详细信息就在test.out里,通过File off可以确定相应段的偏移位置)
桌面环境下LMA就是相应的段加载到虚存的目标位置处 即虚存地址
编译时段就已经确定虚存地址,确定的地址在哪就加载到哪
此时VMA == LMA
在嵌入式环境下
将程序烧写到目标设备的flash
★ Nand Flash 只能存储不能执行
Nand Flash对应的嵌入式设备:将烧写到flash中的程序加载到RAM中执行,上电后我们需要知道执行的代码具体在flash的哪个地址处,然后将具体地址的代码复制到RAM中执行,这个地址叫加载地址(从哪里将段拷贝到RAM)
此时VMA != LMA
★ Nor Flash 可以直接执行
Nor Flash对应的嵌入式设备: 上电后直接执行指令即可,此时加载地址就是Nor Flash里的地址,运行地址也是。此时很可能不存在虚存地址
即VMA != LMA