开发中的辅助工具

一、什么是开发环境

构建环境

  • 代码编写,程序编译,版本控制(可选)

调试环境

  • 用于定位问题的辅助工具集

测试环境

  • 用于验证目标程序是否满足用户的显性需求和隐性需求

二、嵌入式开发的时间分配

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

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

​ 工欲善其事,必先利其器!

四、GNU为GCC编译器提供了配套的辅助工具集

binutils

工具名功能简介
addr2line将代码地址转换为对应的程序行号
strip剔除可执行程序中的调试信息
ar将目标文件打包成静态库
nm列出目标文件中的符号和对应地址
objdump查看程序段信息反汇编
size查看目标文件中的段大小
string查看目标文件中的字符串

1、addr2line

  • 将指定地址转换为对应的文件名行号
  • 常用于分析和定位内存访问错误的问题
//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;
}

//func.c
#include <stdio.h>

int* g_pointer;

void func()
{
	*g_pointer = (int)"D.T.Software";

    return;
}


addr2line示例:定位0地址访问

  1. 开启core dump选项
    • ulimit -c unlimited
  2. 运行程序,并生成崩溃时的core文件
    • 执行导致程序崩溃的测试用例
  3. 读取core文件,获取IP寄存器的值(0x08048000)
    • dmesg core
  4. 使用addr2line定位代码行
    • addr2line 0x08048000 -f -e test.out
/*上机试验
0、gcc test.c func.c -g
	生成a.out,注意-g
1、ulimit -c unlimited
	开启core dump选项
2、./a.out
3、dmesg core
	segfault at 0 ip 00000000004005b3 sp 00007ffde08ae040 error 6 in a.out[400000+1000]
4、addr2line 0x08048000 -f -e test.out
	输出结果
	func
	/mnt/hgfs/VMShare/hello7688/ditai/sifang/func.c:7

*/

2、strip

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

用法:strip a.out


注意事项:

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

3、ar

  • 打包目标文件,生成静态链接库
    • ar crs libname.a x.o y.o
  • 解压目标文件
    • ar x libname.a

4、nm

  • 列出目标文件中的标识符(变量名,函数名)
  • 输出结果由三部分组成:{地址、段、标识符}

示例:

标识符对应的地址标识符位于代码段标识符的名字
08048430Tfunc

段标识说明

段标识说明
A地址值在链接过程中不会发生改变
B/b标识符位于未初始化数据段(.bss)
C未定义存储段的标识符,链接时决定段位置
D/d标识符位于数据段(.data)
N调试专用标识符
R/r标识符位于只读存储区(.rdata)
T/t标识符位于代码段(.text)
U未定义的标识符
//运行
$ gcc -g -c test.c -o test.o
$ gcc -g -c func.c -o func.o
$ ls *o
 func.o  test.o
$ nm test.o
                 U func
0000000000000000 B g_global
                 U g_pointer
0000000000000000 D g_test
0000000000000000 T main
                 U printf
$ nm func.o
0000000000000000 T func
0000000000000008 C g_pointer
//编译得到的目标文件,还未链接
$ gcc -g func.o test.o -o test.out
$ nm test.out 
0000000000601034 B __bss_start
0000000000601038 b completed.7594
0000000000601020 D __data_start
0000000000601020 W data_start
0000000000400440 t deregister_tm_clones
0000000000400430 T _dl_relocate_static_pie
00000000004004c0 t __do_global_dtors_aux
0000000000600e10 t __do_global_dtors_aux_fini_array_entry
0000000000601028 D __dso_handle
0000000000600e20 d _DYNAMIC
0000000000601034 D _edata
0000000000601048 B _end
0000000000400634 T _fini
00000000004004e0 t frame_dummy
0000000000600e08 t __frame_dummy_init_array_entry
0000000000400818 r __FRAME_END__
0000000000400506 T func
000000000060103c B g_global
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
00000000004006a8 r __GNU_EH_FRAME_HDR
0000000000601040 B g_pointer
0000000000601030 D g_test
00000000004003c8 T _init
0000000000600e10 t __init_array_end
0000000000600e08 t __init_array_start
0000000000400640 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000600e18 d __JCR_END__
0000000000600e18 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000400630 T __libc_csu_fini
00000000004005c0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000040051b T main
                 U printf@@GLIBC_2.2.5
0000000000400480 t register_tm_clones
0000000000400400 T _start
0000000000601038 D __TMC_END__
    
--------------------------------------------------------------------------
    
./test.out 
&g_global = 0x60103c
&g_test = 0x601030
&g_pointer = 0x601040
g_pointer = (nil)
&func = 0x400506
&main = 0x40051b
段错误 (核心已转储)

5、objdump

  • 反汇编目标文件,查看汇编到源码的映射
    • objdump -d func.o
    • objdump -S func.o
  • 查看目标文件中的详细段信息
    • objdump -h test.out
//objdump -d func.o
$ objdump -d func.o

func.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <func>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	48 8b 05 00 00 00 00 	mov    0x0(%rip),%rax        # b <func+0xb>
   b:	ba 00 00 00 00       	mov    $0x0,%edx
  10:	89 10                	mov    %edx,(%rax)
  12:	90                   	nop
  13:	5d                   	pop    %rbp
  14:	c3                   	retq   

//objdump -S func.o
$ objdump -S func.o

func.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <func>:
#include <stdio.h>

int* g_pointer;

void func()
{
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
	*g_pointer = (int)"D.T.Software";
   4:	48 8b 05 00 00 00 00 	mov    0x0(%rip),%rax        # b <func+0xb>
   b:	ba 00 00 00 00       	mov    $0x0,%edx
  10:	89 10                	mov    %edx,(%rax)

    return;
  12:	90                   	nop
}
  13:	5d                   	pop    %rbp
  14:	c3                   	retq   

//objdump -h test.out
$ objdump -h test.out

test.out:     文件格式 elf64-x86-64

节:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000060  00000000004002b8  00000000004002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       0000003f  0000000000400318  0000000000400318  00000318  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000008  0000000000400358  0000000000400358  00000358  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  0000000000400360  0000000000400360  00000360  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     00000030  0000000000400380  0000000000400380  00000380  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000018  00000000004003b0  00000000004003b0  000003b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         00000017  00000000004003c8  00000000004003c8  000003c8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000020  00000000004003e0  00000000004003e0  000003e0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         00000232  0000000000400400  0000000000400400  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .fini         00000009  0000000000400634  0000000000400634  00000634  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .rodata       00000068  0000000000400640  0000000000400640  00000640  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame_hdr 00000044  00000000004006a8  00000000004006a8  000006a8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame     0000012c  00000000004006f0  00000000004006f0  000006f0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .init_array   00000008  0000000000600e08  0000000000600e08  00000e08  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 18 .fini_array   00000008  0000000000600e10  0000000000600e10  00000e10  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .jcr          00000008  0000000000600e18  0000000000600e18  00000e18  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .dynamic      000001d0  0000000000600e20  0000000000600e20  00000e20  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got          00000010  0000000000600ff0  0000000000600ff0  00000ff0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got.plt      00000020  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .data         00000014  0000000000601020  0000000000601020  00001020  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .bss          00000010  0000000000601038  0000000000601038  00001034  2**3
                  ALLOC
 25 .comment      00000035  0000000000000000  0000000000000000  00001034  2**0
                  CONTENTS, READONLY
 26 .debug_aranges 00000060  0000000000000000  0000000000000000  00001069  2**0
                  CONTENTS, READONLY, DEBUGGING
 27 .debug_info   000001a1  0000000000000000  0000000000000000  000010c9  2**0
                  CONTENTS, READONLY, DEBUGGING
 28 .debug_abbrev 000000da  0000000000000000  0000000000000000  0000126a  2**0
                  CONTENTS, READONLY, DEBUGGING
 29 .debug_line   00000084  0000000000000000  0000000000000000  00001344  2**0
                  CONTENTS, READONLY, DEBUGGING
 30 .debug_str    00000104  0000000000000000  0000000000000000  000013c8  2**0
                  CONTENTS, READONLY, DEBUGGING

说明
ldx段下标
Name段标识符(名字)
Size段所占空间的大小
VMA段起始位置的虚存地址
LMA段在内存空间中的加载地址
File off段在目标文件中的相对位置
Algn段的边界对齐字节数

image-20210922231824846

6、size

  • 获取目标文件中的所有段大小
    • size test.out
  • 用途:查看段的大小,嵌入式开发受资源限制,尽可能优化以保证每个段都能存入目标设备
$ size test.out 
   text	   data	    bss	    dec	    hex	filename
   1493	    556	     16	   2065	    811	test.out

7、strings

  • 获取目标文件中的所有字符串常量
    • strings test.out
  • 用途:获得字符串常量,嵌入式开发受资源限制,决定多余,需要优化的字符串
$ strings func.o
D.T.Software
func.c
unsigned int
func
long unsigned int
func.c
signed char
short unsigned int
g_pointer
short int
unsigned char
long int
sizetype
GNU C11 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong
char
/mnt/hgfs/VMShare/hello7688/ditai/sifang
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
func.c
g_pointer
func
.symtab
.strtab
.shstrtab
.rela.text
.data
.bss
.rodata
.rela.debug_info
.debug_abbrev
.rela.debug_aranges
.rela.debug_line
.debug_str
.comment
.note.GNU-stack
.rela.eh_frame
//后面的输出没懂
$ strings test.o
&g_global = %p
&g_test = %p
&g_pointer = %p
g_pointer = %p
&func = %p
&main = %p
test.c
unsigned int
long unsigned int
g_test
/mnt/hgfs/VMShare/hello7688/ditai/sifang
char
unsigned char
main
g_global
g_pointer
long int
argc
GNU C11 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong
short unsigned int
signed char
short int
test.c
sizetype
argv
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
test.c
g_global
g_test
main
printf
g_pointer
func
.symtab
.strtab
.shstrtab
.rela.text
.data
.bss
.rodata
.rela.debug_info
.debug_abbrev
.rela.debug_aranges
.rela.debug_line
.debug_str
.comment
.note.GNU-stack
.rela.eh_frame
//后面的输出没懂
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值