开发中的辅助工具

来源:互联网 发布:电视直播软件清晰度 编辑:程序博客网 时间:2024/06/04 19:48

1 什么是开发环境

  • 构建环境
    • 代码编写,程序编译,版本控制(可选)
  • 调试环境
    • 用于定位问题的辅助工具集
  • 测试环境
    • 用于验证目标程序是否满足用户的显性需求和隐性需求

嵌入式开发中的时间分配:

  • 代码编写及目标构建(20%)
  • 测试,调试,bug修复(80%)

问题:如何提高开发效率?

  • 工欲善其事,必先利其器

2 开发中的辅助工具

  • GNU为GCC提供了配套的辅助工具集(Binutils)
  • Binutils官网

这里写图片描述

3 addr2line

  • 将指定地址转换为对应的文件名和行号(只能转换函数内的地址而不能是全局变量的地址)
  • 常用于分析和定位内存访问错误的问题

这里写图片描述

addr2line示例:定位0地址访问

  • 开启core dump
    • ulimit -c unlimited
  • 运行程序,并生成崩溃时的core文件
    • 执行程序崩溃的测试用例
  • 读取core文件,获取IP寄存器的值(0x08048000)
    • dmesg core
  • 使用addr2line定位代码行
    • addr2line 0x08048000 -f -e test.out(-f:显示函数名,-e:指定文件)

4 strip

  • 剔除程序文件中的调试信息,减少目标程序的大小
  • 一般在程序发布前都需要将调试信息剔除
  • 过多的调试信息可能影响程序的执行效率
  • strip test.out

注意事项

  • 几乎所有的调试工具都依赖目标文件中的调试信息
  • 调试信息的运用能够快速定位问题
  • 使用gcc编译程序时使用-g选项生成调试信息
  • 发布程序时再考虑是否使用strip剔除调试信息

5 ar

  • 打包目标文件
    • ar crs libname.a x.o y.o(ar的c参数表示创建一个档案文件,r参数指示将文件增加到所创建的库中,s参数是为了生成库索引以提高库被链接时的效率)
  • 解压目标文件
    • ar x libname.a

6 nm

  • 列出目标文件中的标识符(变量名、函数名)
  • 输出结构由三部分组成:{地址、段、标识符}
  • nm在编译时不需要加-g选项

这里写图片描述

段标识说明

这里写图片描述

int* g_pointer;int a;char c;void func(void){    *g_pointer = (int)"hello, lemon";}执行如下命令:gcc -g -c func.cnm fun.o运行结果如下:00000004 C a(对于段标识为C来说,4指的是所占内存的字节数,如下同理)00000001 C c00000000 T func00000004 C g_pointer

关于nm的疑问

代码如下:#include <stdio.h>int main(void){    printf("All is well...\n");    return 0;}执行如下命令:gcc -c test.c -o test.onm test.onm的结果如下:00000000 T main         U putsprintf函数怎么变成了puts函数呢?这里发生了什么呢?执行的命令如下:gcc test.c -o test.outnm test.outnm的结果如下:0804a020 B __bss_start0804a020 b completed.68770804a018 D __data_start0804a018 W data_start08048360 t deregister_tm_clones080483d0 t __do_global_dtors_aux08049f0c t __do_global_dtors_aux_fini_array_entry0804a01c D __dso_handle08049f14 d _DYNAMIC0804a020 D _edata0804a024 B _end080484c4 T _fini080484d8 R _fp_hw080483f0 t frame_dummy08049f08 t __frame_dummy_init_array_entry080485d4 r __FRAME_END__0804a000 d _GLOBAL_OFFSET_TABLE_         w __gmon_start__080482b0 T _init08049f0c t __init_array_end08049f08 t __init_array_start080484dc R _IO_stdin_used         w _ITM_deregisterTMCloneTable         w _ITM_registerTMCloneTable08049f10 d __JCR_END__08049f10 d __JCR_LIST__         w _Jv_RegisterClasses080484c0 T __libc_csu_fini08048450 T __libc_csu_init         U __libc_start_main@@GLIBC_2.00804841b T main         U puts@@GLIBC_2.008048390 t register_tm_clones08048320 T _start0804a020 D __TMC_END__08048350 T __x86.get_pc_thunk.bx已经完成了链接工作,为什么还有这么多未定义的标识符呢?

7 objdump

  • 反汇编目标文件,查看汇编到源码的映射

    • objdump -d func.o(不需要包含调试信息)
    • objdump -S func.o(需要包含调试信息)
  • 查看目标文件中的详细信息

    • objdump -h test.out
  • objdump -h输出说明

这里写图片描述

可执行程序是如何加载到内存中的?
三个重要的概念:虚存地址(VMA)、加载地址(LMA)、运行地址

桌面环境 :test.out(可执行文件),执行test.out会为其创建一个进程。首先会分配虚存,然后将相应的段加载到段所对应的虚存地址,这个虚存地址是在编译时确定的。这个虚存地址也就是加载目标地址,是终点的地址,简称加载地址。 最后执行应用程序。运行地址是指实地址。

嵌入式环境:源代码交叉编译得到test.bin,然后烧写到device的flash中。当flash为nandflash时,由于nand flash 只能存储代码不能执行代码,所以需要将代码从flash加载到ram中。 从flash的地址将代码拷贝到ram中,这个地址就叫加载地址,指的是起点。vma不等于lma ,这里的运行地址为实地址;当flash为norflash时, 代码可以直接在norflash中执行, 加载地址就是运行地址,虚存地址可能没有。

8 size

  • 获取目标文件中的所有段大小(单位为byte)
    • size test.out

9 strings

  • 获取目标文件中的所有字符串常量
    • strings test.out
原创粉丝点击