一、什么是开发环境
构建环境
- 代码编写,程序编译,版本控制(可选)
调试环境
- 用于定位问题的辅助工具集
测试环境
- 用于验证目标程序是否满足用户的显性需求和隐性需求
二、嵌入式开发的时间分配
- 代码编写及目标构建(20%)
- 测试,调试,bug修复(80%)
三、问题:如何提高开发效率?
工欲善其事,必先利其器!
四、GNU为GCC编译器提供了配套的辅助工具集
工具名 | 功能简介 |
---|---|
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地址访问
- 开启core dump选项
- ulimit -c unlimited
- 运行程序,并生成崩溃时的core文件
- 执行导致程序崩溃的测试用例
- 读取core文件,获取IP寄存器的值(0x08048000)
- dmesg core
- 使用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
- 列出目标文件中的标识符(变量名,函数名)
- 输出结果由三部分组成:{地址、段、标识符}
示例:
标识符对应的地址 | 标识符位于代码段 | 标识符的名字 |
---|---|---|
08048430 | T | func |
段标识说明
段标识 | 说明 |
---|---|
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 | 段的边界对齐字节数 |
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
//后面的输出没懂