对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高。目前,可以说嵌入式系统的开发工具是GNU的天下,因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC,还有一个非常重要的、同样来自于GNU的工具集(toolchain) —— binutils toolchain。这一工具集中存在的一些工具,可以说是我们开发和调试不可缺少的利器。 Binutils中的工具不少和GCC相类似,也是针对特定的处理器的。
在binutils中以下的工具是我们在做嵌入式系统开发时需要掌握的:
addr2line | 将程序地址翻译成文件名和行号;给定地址和可执行文件名称,它使用其中的调试信息判断与此地址有关联的源文件和行号 |
ar | 创建、修改和提取归档 |
一个汇编器,将 gcc 的输出汇编为对象文件 into object files | |
c++filt | 被链接器用于修复 C++ 和 Java 符号,防止重载的函数相互冲突 |
elfedit | 更新 ELF 文件的 ELF 头 |
显示分析数据的调用图表 | |
ld | 一个链接器,将几个对象和归档文件组合成一个文件,重新定位它们的数据并且捆绑符号索引 |
到 ld 的硬链接 | |
nm | 列出给定对象文件中出现的符号 |
将一种对象文件翻译成另一种,.bin 转换成 .elf 、.elf 转换成 .bin等。 | |
objdump | 显示有关给定对象文件的信息,包含指定显示信息的选项;显示的信息对编译工具开发者很有用,最主要的作用是反汇编 |
创建一个归档的内容索引并存储在归档内;索引列出其成员中可重定位的对象文件定义的所有符号 | |
readelf | 显示有关 ELF 二进制文件的信息,readelf -h *.exe进行查看。 |
列出给定对象文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等。 | |
strings | 对每个给定的文件输出不短于指定长度 (默认为 4) 的所有可打印字符序列;对于对象文件默认只打印初始化和加载部分的字符串,否则扫描整个文件 |
移除对象文件中的符号,进行文件压缩,进行瘦身; | |
| 包含多个 GNU 程序会使用的途径,包括 getopt、obstack、strerror、strtol 和 strtoul |
二进制文件描述器库 | |
一个库,用于处理 opcodes——处理器指令的 “可读文本” 版本;用于编制 objdump 这样的工具 |
下面是各命令的详细使用方法:
1、 addr2line
addr2line是用来将程序地址转换成其所对应的程序源文件及所对应的代码行,当然,也可以得到所对应的函数。为了说明addr2line是如何使用的,我们需要有一个练习用的程序。先采用编辑工具编辑一个test.c源文件,其内容如图所示。
运行如下的命令将test.c编译成可执行文件,注意:必须加 -g 这个选项 并运行之。在运行test程序后,我们可以在其终端上看到它打印出的fun()函数的地址 —— 0x80483e4。
现在,我们可以用这一地址来看一看addr2line是如何使用的。在终端中运行如下的命令,从命令的运行结果来看,addr2line工具正确的指出了地址0x80483e4 所对于应的程序的具体位置是在哪以及所对应的函数名是什么。
可能有人会问了:这个0x80483e4地址是我们打印出来,即然有打印,我们一般情况下也会打印出其具体的函数位置,而不是只打印地址,我为何要这么绕一下通过addr2line去找到地址所对应的函数呢?其实,这里打印出地址只是为了得到一个地址以便用于练习。在现实中,地址往往是在调试过程中或是当程序崩溃时通过某种方式获得的。此外,采用nm工具(后面会讲到)可以得到如下的函数地址信息。
2、as
as汇编器,将汇编代码汇编成目标文件
3、 size
size工具,就是列程序文件中各段的大小。在后面的章节中,我们会使用objdump查看段信息,除了这三个段还有.rdata和.idata两个段,其中.rdata段被归类到.text段中,而.idata段被归类到.data段中。下面是采用size工具所显示出的test中的段大小信息。
4、nm
nm用于列出程序文件中的符号,符号是指函数或是变量名什么的。下面,我们来看一看图 2所编译出来的程序当中有些什么符号
nm所列出的每一行有三部分组成:
第一列是指程序运行时的符号所对应的地址,对于函数则地址表示的是函数的开始地址,对于变量则表示的是变量的存储地址;
第二列是指相应符号是放在哪一个段的;
第三列则是指符号的名称。
在前面我们讲解addr2line时,我们提到addr2line是将程序地址转换成这一地址所对应的具体函数是什么,而nm则是全面的列出这些信息。但是,nm不具备列出符号所在的源文件及其行号这一功能,因此,我们说每一个工具有其特定的功能,在嵌入式系统的开发过程中我们需要灵活的运用它们。
对于nm列出的第二列信息,非常的有用,其意义在于可以了解我们在程序中所定义的一个符号(比如变量等等)是被放在程序的哪一个段的,下表列出了第二列将会出现的部分字母的含义,要参看所有字母的意思,请在你的开发环境中运行“man nm”。
5、strip
strip的功能也相对的简单,主要用于去除程序文件中的调试信息以便减小文件的大小。对于strip的功能,其与objcopy带--strip-debug参数时的功能是一样的,这我们前面也有提及。strip所具有的功能,objcopy也都有。
可以看到test小了几KB,strip在大文件中有更好的体现。
6、objdump
objdump可以用来查看目标程序中的段信息和调试信息,也可以用来对目标程序进行反汇编。我们知道程序是由多个段组成的,比如.text是用来放代码的、.data是用来放初始化好的数据的、.bss是用来放未初始化好的数据的,等等。在嵌入式系统的开发过程中,我们有时需要知道所生成的程序中的段信息来分析问题。比如,我们需要知道其中的某个段在程序运行时,共起始地址是什么,或者,我们需要知道正在运行的程序中是否存在调试信息等等。
1)-- 下面是使用objdump的--h选项来查看程序中的段信息,练习用的程序如前面的图,这里假设你已将其编译成了可执行文件test
2)-- 反汇编
7、objcopy
objcopy的功能非常的强大,它可以对最后生成的程序文件进行一定的编辑。
作用:格式转换
例:objcopy -O binary xx xx.bin
8、readelf
readelf可以显示elf格式可执行文件的信息。ELF格式是UNIX系统实验室作为应用程序二进制接口开发的。ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一。