熟悉binutils工具集(完整版)

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

熟悉binutils工具集█1熟悉binutils工具集李云Blog:yunli.blog.51cto.com摘要对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高。目前,可以说嵌入式系统的开发工具是GNU的天下,因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC,还有一个非常重要的、同样来自于GNU的工具集(toolchain)——binutilstoolchain。这一工具集中存在的一些工具,可以说是我们开发和调试不可缺少的利器。本文通过介绍binutils以及提供一定的使用实例来帮助读者熟悉这一工具集,以达到提高效率的目的。当你掌握了binutils后,你会发现你得到的是“渔”而不只是“鱼”。关键词binutils工具集参考资料《什么是bootloader》《堆和栈》《程序中的段》《C语言中一个字节对齐问题的分析》1引言对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高。目前,可以说嵌入式系统开发工具是GNU(www.gnu.org)的天下,因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC,还有一个非常重要的、同样来自于GNU的工具集(toolchain)——binutilstoolchain。Binutils中的工具不少和GCC相类似,也是针对特定的处理器的。你可能要问:哪些嵌入式操作系统的开发是采用GNU工具集(包括GCC编译器、binutils工具集等)的?Linux相关的实时(MontaVistaLinux、WindRiverLinux、RTLinux等)或非实时嵌入式系统开发就不用说了,全是采用GNU工具集的;最为有名的来自WindRiver(现已被Intel收购)的VxWorks操作系统也是采用GNU工具集的,为了使用GNU工具集,VxWork的开发IDE采用Cygwin作为其在Windows操作系统的支撑平台;还有就是RTEMS(www.rtems.org)操作系统,以前是美国军方的一个实时操作系统,后来开源了,也是采用GNU工具集的;此外,另一个很有名的实时操作系统——eCos,也是采用GNU工具集的,如果你熟悉Altera的Nios,那么对eCos也应当不陌生;等等。我想可以举出很多很多的例子。例子越多,说明我们学习binutils就越是有用!还有对于bintuils工具集的学习,不光是对于嵌入式系统开发有用,对于Linux主机或是Solaris服务器上的程序开发也是很有帮助的。对于采用C/C++从事Windows应用程序开发的人来说,很有可能会问:我在Windows上的一个目标文件其后缀是.obj,在GNU的工具集中仍是采用.obj后缀吗?在Windows中的动态库是以.dll结尾的,那在GNU的工具集中也一样吗?。这些都是很好的问题,通过类比,我们可以根据我们的经验去掌握另一类似的新东西。在GNU工具集中,一个源程序(.c或是.cpp)是先被编译成.o目标文件(对应于Windows中的.obj文件)的,如果目标文件直接连接成可执行文件,则生成的是ELF(ExcutableandLinkableFormat)文件。这种可执行文件对应于Windows中的.exe文件,与Windows系统所不同的是,在GNU工具集中一个可执行文件并没有一个统一的后缀,甚至没有后熟悉binutils工具集2█缀。如果要将多个.o文件生成一个库文件,那么存在两种类型的库:一种是静态库,其后缀是.a;另一种是动态库,其后缀是.so。在Windows系统中,其全部都是.dll。静态库与动态库的区别是什么呢?静态库是每一个与这一库进行连接的都将有一份代码(和数据)拷贝。比如,如果libx.a中存在一个foo()函数,而程序A和程序B都需要采用libx.a进行连接以使用其中的foo()函数,那么在连接以后,程序A和B的可执行程序中都会存在一个foo()函数,即程序A和程序B的可执行代码中都存在foo()函数的一个拷贝。与静态库所不同的是,采用动态库则不会生成多个代码拷贝。采用动态库时,如前面的程序A和B,所有的程序共享这个库的代码,即在内存中只存在这个库中代码的一个拷贝,但这个库中的(可读写)数据仍然是每一个程序拥有一个独立的拷贝。在binutils中以下的工具是我们在做嵌入式系统开发时需要掌握的:as是汇编器,在此我不打算对其进行讲解,因为其涉及到了处理器的指令集,我们在合适的时候再来讲。addr2line用得到程序地址所对应源代码的文件名和行号以及所对应的函数。ar用于创建、修改档案文件(比如.a静态库文件)以及从档案文件中抽取文件(比如从.a静态库中抽取.o文件)。ld是连接器,对其的讲解我打算采用独立的一篇文章来进行,因为连接器在嵌入式系统开发中非常重要。比如,我们需要通过写或是修改连接脚本,来定制我们的嵌入式程序中的各个段(section)。nm用于列出目标文件、库或是可执行文件(后面统称这三种文件为程序文件)中的代码符号及代码符号所对应的程序开始地址。objcopy是用来拷贝或是翻译目标文件的。objdump帮助我们显示程序文件的相关信息。ranlib用于生成一个档案文件的内容索引。这样做的目的是为了加快档案文件的访问速度,比如,我们常对静态库文件(.a文件)进行ranlib以提高连接速度。readelf用于显示ELF文件的信息。size用于显示程序文件的段信息。strings用于显示一个程序文件当中的可显示字符串。strip用于剥去程序文件中的符号信息,以减小程序文件的大小。这对于存储空间有限的嵌入式系统尤为有用。在接下来的章节,我们将看一看各个工具的使用方法和使用例子。需要注意的是,本文并不是binutils工具集的完整参考手册,对于每一个工具的讲解都是基于其常用功能来进行的,当你需要得到更为详细的帮助信息时,完全可以参照相应工具的man(或info)信息。比如,你要获得objdump工具的man信息,你可以在Linux或是Cygwin中运行“manobjdump”。另一种更为简单的方法是采用--help参数运行相应工具得到简单的帮助信息,比如“objdump--help”。需要注意的是,这不是一篇教你如何进行Linux程序开发的文章,相反,这里假设了你了解一些基本的Linux命令。同样地,这篇文章不会告诉你什么时候要用GCC进行编译,而什么时候又得用G++进行编译,更不会告诉你这些编译器的具体参数的意思是什么以及如何使用。对于这些信息,你需要参考其它的文章或是书籍。2准备环境在讲解binutils中的工具之前,我们需要有一定的环境用于练习。你可以找一台安装有GCC的Linux计算机(可以是Wmware上虚拟的),如果没有,你可以在你的Windows上安装一个Cygwin。如果你需要安装Cygwin,通常分为以下几个步骤:从www.cygwin.com上下载setup.exe,并运行它。然后选择“从Internet下载安装包到本地”。在下载之前,请确保你选择了下载GCC和binutils安装包。安装包下载完了以后,你需要再次运行setup.exe,且这次选择“从本地安装”。在安装时,熟悉binutils工具集█3同样不要忘了选择安装GCC和binutils。当你安装好了Cygwin后,运行Cygwin并在其上运行如下的命令(注:美元符‘$’不是命令的一部分,它是命令提示符,这如同Windows命令窗口中的‘C:\’提示符)来验证binutils是否已准备好。不管你使用的是Linux操作系统或是Cygwin,如果你能看到命令运行后出现了对于这一命令的使用说明,那么说明binutils在你的环境中被正确地安装了。yunli.blog.51cto.com~$nm-h如果采用Cygwin,由于Cygwin只是在Windows上模拟Linux的环境,因此,其有些行为仍然是像Windows的。比如,如果我们采用以下的命令来编译一个程序,在Linux上其生成的可执行文件名就是test,而在Gygwin上则为test.exe。在后面的使用实例中,你需要注意这一区别。了就是说,在Linux中可能输入的是test,而在Cygwin中你必须换成输入test.exe;反之亦然。yunli.blog.51cto.com~$gccmain.c-otest3addr2lineaddr2line是用来将程序地址转换成其所对应的程序源文件及所对应的代码行,当然,也可以得到所对应的函数。为了说明addr2line是如何使用的,我们需要有一个练习用的程序。先采用编辑工具编辑一个main.c源文件,其内容如图1所示。main.c#includestdio.hvoidfoo(){printf(“Theaddressoffoo()is%p.\n”,foo);}intmain(){foo();return0;}图1运行如下的命令将main.c编译成可执行文件,并运行之。在运行test.exe程序后,我们可以在其终端上看到它打印出的foo()函数的地址——0x401100。yunli.blog.51cto.com~$gcc-gmain.c-otestyunli.blog.51cto.com~$./test.exeTheaddressoffoo()is0x401100.现在,我们可以用这一地址来看一看addr2line是如何使用的。在终端中运行如下的命令,从命令的运行结果来看,addr2line工具正确的指出了地址0x401100所对于应的程序的具体位置是在哪以及所对应的函数名是什么。yunli.blog.51cto.com~$addr2line0x401100-f-etest.exe熟悉binutils工具集4█foo/home/Administrator/main.c:4可能有人会问了:这个0x401100地址是我们打印出来,即然有打印,我们一般情况下也会打印出其具体的函数位置,而不是只打印地址,我为何要这么绕一下通过addr2line去找到地址所对应的函数呢?其实,这里打印出地址只是为了得到一个地址以便用于练习。在现实中,地址往往是在调试过程中或是当程序崩溃时通过某种方式获得的。此外,采用nm工具(后面会讲到)可以得到如下的函数地址信息。yunli.blog.51cto.com~$nm-ntest.exe…显示结果有删减…00401100T_foo0040111CT_mainnm命令会打印出所有的符号(包括函数和全局变量名)所对应的开始地址,需要注意的是,在C中源代码中的函数其所对应的nm输出符号前会多加一个下划线‘_’,比如C程序中的main()函数对应的是nm输出符号中的_main,同样地,C程序中的foo()函数对应的是nm输出符号中的_foo。从nm输出的信息你可以看出,foo()函数所对应的地址为0x00401100,而foo()函数是有大小的(因为其有实现代码,且代码越是复杂或是长,则函数的大小越大),其大小是_main的地址减_foo的地址(_main紧跟在_foo的后面说明在C程序中main()函数是跟在foo()函数的后面的),那么是不是说我们给add2line的地址可以是从0x0040100到0x0040111C的任一地址呢?是的,请看下面的操作结果。yunli.blog.51cto.com~$addr2line0x401110-f-etest.exefoo/home/Administrator/main.c:4yunli.blog.51cto.com~$addr2line0x40111B-f-etest.exefoo/home/Administrator/main.c:4我们已经讲了对于C程序addr2line是如何使用的,那么对于C++程序呢?现在假设我们有如图2所示的C++代码。main.cpp#includeiostreamusingnamespacestd;voidfoo(){cout“Theadd

1 / 20
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功