我的binutils工具集

本文详细介绍了C/C++编程语言在编译过程中的关键步骤,包括可执行文件、目标文件、静态库、动态链接、调试工具等核心概念。文章通过实例演示了如何使用addr2line、nm、readelf、ar、objdump、objcopy、size、strip和strings等工具进行代码定位、符号管理、文件检查和优化,旨在帮助开发者深入理解C/C++程序的内部运作机制。

1.addr2line能够把程序地址转换为文件名和行号,前提是这个可执行文件包括调试符号

 1 #include <stdio.h>

  2 

  3 void foo()

  4 {

  5     printf("The address of foo() is %p\n",foo);

  6 }

  7 int main()

  8 {

  9     foo();

 10     return 0;                                                               

 11 }

运行如下命令,得到:

现在,我们可以用这一地址来看看addr2line是如何使用的。在终端中运行如下命令,从命令的运行结果可以看出,addr2line工具正确指出了0x80483c4所对应的程序的具体位置以及所对应的函数名。在调用 addr2line 工具时,要使用 -e 选项来指定可执行映像是 test。通过使用 -f 选项,可以告诉工具输出函数名。

2.nm可以列出目标文件中的符号。用法虽然简单,但是功能很强大。符号是指函数名或变量。 

nm所列出的每一行有三部分组成:第一列是指程序运行时的符号所对应的地址,对于函数则地址表示的是函数的开始地址,对于变量则表示变量的存储地址;第二列是指对应符号放在哪一个段;而最后一列则是指符号的名称。在前面我们讲解addr2line时,我们提到addr2line是将程序地址转换成这一地址所对应的具体函数是什么,而nm则是全面的列出这些信息。但是,nm不具备列出符号所在源文件及其行号这一功能,因此,我们说每一个工具有其特定功能。

 为了更清楚的理解nm中的符号和我们程序中的关系,我们看一下下列程序其所对应的nm输出结果。

1 #include <stdio.h>

  2 int gloable1;

  3 int gloable2 = 9;

  4 

  5 static int static_gloable1;

  6 static int static_gloable2 = 99;

  7 

  8 void foo()

  9 {

 10     static int intermal1;

 11     static int intermal2 = 999;

 12 }

 13 

 14 static void bar()

 15 {

 16     

 17 }

 18 

 19 int main()

 20 {

 21     int local;

 22     int local2 = 9999;

 23     foo();

 24     return 0;                                                               

 25 }

从nm输出的信息,我们可以看出:

Ø 不论一个静态变量是定义在函数内还是函数外,其在程序段中的分配方式都是一样的。如果这一静态变量是初始化好的,那么被分配在data段中,否则就在bss段中

Ø 非静态的全局变量,其分配的段也是和是否初始化有关。如果被初始化了,分配在data段中,否则就在bss段中。

Ø 函数无论是静态还是非静态的,其总是被分配在text段,但T(t)的大小写代表这一符号所对应的函数是否是静态函数。

Ø 函数内的局部变量并不是分配在databsstext段中,其分配在栈上,nm是看不到的。

3. readelf –h test

这条命令查看可执行文件test”section的头信息。

每一个头信息都是一个Elf32_Shdr结构,其成员含义如下所述:

typedef struct {

      Elf32_Word sh_name; //指定了这个section的名字

      Elf32_Word sh_type; //sections按内容和意义分类

      Elf32_Word sh_flags; //sections支持位的标记,用来描述多个属性

      Elf32_Addr sh_addr; //section 在内存中的位置

      Elf32_Off  sh_offset; //section的字节偏移量

      Elf32_Word sh_size; //section的字节大小

      Elf32_Word sh_link; //section报头表的索引连接

      Elf32_Word sh_info; //保存着额外的信息

      Elf32_Word sh_addralign; //地址对齐的约束

      Elf32_Word sh_entsize; //保存着一张固定大小入口的表

  } Elf32_Shdr;

4.ar用来管理档案文件,在嵌入式系统当中,ar主要用来对静态库进行管理。现在先让我们看看静态库里有些什么。我们采用lib.a为例,对其用ar -t来查看,如下所示:

采用GNU工具集进行开发时,一个静态库其实是将所有的.o文件打成一个档案包。现在,我们就来看看如何使用ar来生成静态库。

foo.c

#include  <stdio.h>

void foo()

{

printf("This is foo()\n");

}

bar.c

#include  <stdio.h>

void  bar()

{

printf("This is bar()\n");

}

我们希望将foo()和bar()函数做成一个库,为此先要将它们编译成.o目标文件。有了目标文件之后,我们采用ar命令来生成libmy.a库,如下所示。其中,arc参数表示创建一个档案文件,而r参数表示增加文件到创建的库文件中,s参数是为了生成库索引以提高连接速度。

   

现在可以在当前目录下看到一个libmy.a文件,这就是我们的静态库。下面验证库是否可用,

 #include <stdio.h>

  2 int main()                                                                  

  3 

  4 {

  5     foo();

  6     bar();

  7     return 0;

  8 }  

     编译我们的验证程序,并与libmy.a进行连接,运行程序,从运行的运行结果可以看出,我们的libmy.a是起作用的。

如果想删除档案文件中的文件,我们可以使用d参数。下面是使用d参数删除libmy.a中的foo.o文件。从操作的最后结果看,当执行完d操作后,libmy.a中只存在一个bar.o文件了。

现在总结一下ar的几个参数:采用c参数创建一个档案文件,r参数表示向档案文件增加文件,t参数用于显示档案文件中存在哪些文件,s参数用于指示生成索引以加快查找速度,d参数用于从档案文件中删除文件,最后x参数是用于从档案文件中解压文件。

5.objdump可以显示一个或者更多目标文件的信息,主要用来反汇编。

如下所示(部分未显示):

6.objcopy可以进行目标文件格式转换。

arm-linux-objcopy --gap-fill=0xff -O srec u-boot u-boot.srec

arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

7.size工具很简单,就是列出程序文件中各段的大小。

 1 #include <stdio.h>

  2 int gloable1;

  3 int gloable2 = 9;

  4 

  5 static int static_gloable1;

  6 static int static_gloable2 = 99;

  7 

  8 static void foo()

  9 {

 10     static int intermal1;

 11     static int intermal2 = 999;

 12 }

 13 

 14 static void bar()

 15 {

 16 }                                                                                                                                         

 17 

 18 int main()

 19 {

 20     int local;

 21     int local2 = 9999;

 22     foo();

 23     return 0;

 24 }

 

8.strip用来丢弃目标文件中的全部或者特定符号,减小文件体积。对于嵌入式系统,这个命令必不可少。

9.strings用来打印某个文件的可打印字符串。

### binutils 工具集介绍 binutils 是 GNU 工具链中的重要组成部分,广泛用于嵌入式系统开发和底层编程。它包含了一系列用于处理目标文件、汇编代码和二进制文件的工具。这些工具在开发和调试过程中非常关键,能够帮助开发者更好地理解和优化程序的结构和性能。 以下是 binutils 工具集中一些常用的工具及其功能: - **as**:GNU 汇编器,用于将汇编语言代码转换为目标机器码。 - **ld**:GNU 链接器,负责将多个目标文件和库文件链接成一个可执行文件。 - **objdump**:用于反汇编目标文件,显示其内容,包括机器指令和符号信息。 - **nm**:列出目标文件中的符号,帮助开发者查看函数和变量的地址。 - **size**:显示目标文件的段大小,有助于分析程序的内存占用。 - **strip**:去除目标文件中的调试信息,减小文件体积,适用于发布版本。 - **objcopy**:用于复制和转换目标文件的格式,常用于生成特定格式的二进制文件。 这些工具不仅支持多种处理器架构,还提供了丰富的命令行选项,使得开发者可以根据具体需求进行灵活配置和使用。 ### binutils 下载 要下载 binutils,可以通过 GNU 官方网站获取最新的源代码包。访问 [GNU binutils 下载页面](https://ftp.gnu.org/gnu/binutils/),选择合适的版本进行下载。通常,推荐使用最新的稳定版本以获得更好的兼容性和功能支持。 此外,对于 Windows 用户,可以使用 MinGW(Minimalist GNU for Windows)来安装 binutils。MinGW 提供了一个轻量级的开发环境,允许在 Windows 上使用 GNU 工具链。可以通过 SourceForge 平台下载 MinGW 安装程序,并按照安装向导完成安装过程 [^2]。 ### binutils 使用教程 #### 安装 1. **从源代码编译安装**: - 下载源代码包后,解压并进入解压后的目录。 - 创建一个新的构建目录,进入该目录并运行配置脚本: ```bash ../configure --target=your-target --prefix=your-install-path ``` 其中 `your-target` 是目标处理器架构(如 `arm-none-eabi`),`your-install-path` 是安装路径。 - 运行 `make` 命令进行编译: ```bash make ``` - 最后运行 `make install` 命令安装: ```bash make install ``` 2. **使用 MinGW 安装**: - 下载 MinGW 安装程序后,运行并选择需要的组件,包括 binutils。 - 点击安装按钮,等待安装完成。 - 将 MinGW 的 `bin` 目录添加到系统的环境变量中,以便在命令行中直接使用。 #### 常用命令示例 - **反汇编目标文件**: ```bash objdump -d your-object-file.o ``` 该命令将显示目标文件的反汇编代码。 - **列出符号表**: ```bash nm your-object-file.o ``` 该命令将列出目标文件中的所有符号及其地址。 - **显示段大小**: ```bash size your-object-file.o ``` 该命令将显示目标文件各个段的大小。 - **去除调试信息**: ```bash strip your-object-file.o ``` 该命令将去除目标文件中的调试信息,减小文件体积。 - **复制并转换目标文件**: ```bash objcopy -O binary your-object-file.o output.bin ``` 该命令将目标文件转换为二进制格式并保存为 `output.bin`。 通过这些工具和命令,开发者可以更有效地进行嵌入式系统开发和调试,确保程序的性能和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值