如果使用 gcc 作为编译器,那么 binutils 就是必不可少的一个工具集。工具集中的部分工具除了被 gcc 在后台使用为我们创建程序文件(目标文件、库文件或可执行程序)外,其它的则有助于方便开发和调试。
在不少嵌入式开发环境中,根据目标平台的不同,编译器的名称往往不是 gcc,而是像 arm-rtems-gcc 这样的名称。对于这种命名形式的编译器,其 binutils 工具集通常也采用 arm-rtems-xxx 命名。
在 binutils 工具集中,以下工具是我们在做嵌入式软件开发时需要掌握的。

1、addr2line,指令地址翻译器

用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号。假设现在有函数:
--main.c--

#include <stdio.h>void foo()
{printf("The address of foo() is %p.\n", foo);  //打印foo函数所在地址
}int main()
{foo();return 0;
}

使用 addr2line 时,可执行程序中必须含有调试信息(关于调试信息,可以参考 objdump),这需要在编译时添加 -g 选项。
$ gcc -g main.c -o test
$ ./test
$$ The address of foo() is 0x804841d.
现在可以直接通过 addr2line 工具查看这个地址的内容(所在函数、函数所在文件名和行号):
$ addr2line 0x404841d -f -e test
$$ foo
$$ /home/binutils/main.c:5
[ -f 表示查看函数名, -e 表示设置输入文件(默认a.out)]
这里的地址是通过程序打印而获得的,在现实工作往往是程序崩溃时通过某种方式获取的,在这种情况下就可以使用 addr2line工具查看崩溃点,进而修正程序。
通过 nm 工具(参考nm),可以得到程序的符号信息:
$ nm -n test
$$ 0804841d T foo
$$ 08048439 T main
[省略其它内容]
事实上,0x0804841d~0x08048439 这一段地址都属于 foo 的汇编内容,addr2line 通过之间的任意地址都可以定位到这个函数。
针对 C++ 程序,addr2line 似乎有些不同。假设有 main.cpp 如下:
--main.cpp--

#include <iostream>using namespace std;void foo()
{cout << "The address of foo() is 0x" << hex << int(foo) <<endl;
}int main()
{foo();return 0;
}

$ g++ -g main.cpp -o cpptest
$ ./cpptest
$$ The address of foo() is 0x804872d
$ addr2line 0x804872d -f -e cpptest
这时候打印出来的却是:
$$ _Z3foov
$$ /home/binutils/main.cpp:6
C++ 为了重载的需要,C++ 处理器会对每一个函数按照一定的编码方式进行重命名,这一过程就是名字分裂过程。而 _Z3foov 则正是名字分裂之后的形式。通过添加  --demangle 选项可以获得原名:
$ addr2line 0x804872d --demangle=gnu-v3 -f -e cpptest 
$$ foo()

$$ /home/binutils/main.cpp:6

2、ar,静态库生成器

Linux 中存在两种类型的库,一种是静态库,后缀为 .a;另一种是动态库,后缀为 .so;关于这两者的区别大概就像内联函数和普通函数那样,可执行程序与静态库链接时,所使用的库中的函数和数据会被拷贝到最终的可执行程序中,而动态库中的程序并不会被拷贝,整个系统中只有它里面唯一的一份。无疑动态库更加节省内存,但在嵌入式系统中,大多数情况整个软件就是一个可执行程序且不支持动态加载,即以静态为主。
binutils 中的 ar 被用来管理静态库。一个静态库实际上就是将 *.o 文件打包而生产的档案文件。
现假设有两个文件 foo.c 和 bar.c,
--foo.c--

#include <stdio.h>void foo()
{printf("This is foo().\n");
}

--bar.c--

#include <stdio.h>void bar()
{printf("This is bar().\n");
}

先将它们各自编译成目标文件:

$ gcc -c foo.c
$ gcc -c bar.c
如此将会生成 foo.o 和 bar.o。将它们合并到 libmy.a 中:
$ ar crs libmy.a foo.o bar.o
其中, c -- 创建一个档案文件; r -- 将文件添加到所创建的库文件中; s -- 生成库索引以提高链接效率。可以通过如下命令查看库中内容:
$ ar t libmy.a
$$ foo.o
$$ bar.o
参数 d 可以删除库中某个目标文件,比如:ar d libmy.a foo.o
参数 x 可以解压这个库文件,比如:ar x libmy.a 
现在假设 main.c 更改如下:
--main.c--

extern void foo();
extern void bar();int main()
{foo();bar();return 0;
}

编译并链接 libmy.a:
$ gcc main.c libmy.a -o test
$ ./test
$$ This is foo().
$$ This is bar().

3、nm,符号显示器

总体来说,nm 用于列出程序文件中的符号。先大概感受一下 nm:
$ nm -n test
$$ 0804841d T main
$$ 08048434 T foo
$$ 08048448 T bar
[省略其它内容...]
nm 所列出的每一行由三部分组成,第一列是函数或者变量的开始地址,第二列是相应的符号位于哪个段,最后一列则是符号的名称。关于第二列,这里的 T 表示代码段,其它类似的还有:
A -- 符号对应的值是绝对的且在以后的链接过程中不会改变
B/b -- 符号位于未初始化的数据段 (.bss 段) 中
C -- 没有被初始化的公共符号
D/d --符号位于初始化的数据段 (.data 段) 中
N -- 符号是调试用的
P -- 符号位于一个栈回溯段内
R/r -- 符号位于只读数据段 (.rdata 段) 中
T/t -- 符号位于代码段 (.text段) 中
U -- 符号没有被定义

为了更好地说明以上符号,现假设有 main.c 如下:

--main.c--

#include <time.h>int global1;                  //1、未初始化的全局变量分配在.bss段//   没有被链接时为C,链接后为B
int global2 = 3;               //2、初始化的全局变量分配在.data段,
const int GLOBAL = 5;          //3、只读数据存放于.rdata段static int static_global1;      //4、未初始化的静态变量分配在.bss段
static int static_global2 = 3;  //5、初始化的静态变量分配在.data段void foo()                        //6、函数被放在.text段,T--非静态
{static int internal1;      //同4static int internal2 = 3;  //同5
}static void bar()              //8、函数被放在.text段,t--静态
{
}int main()                     //同6
{int local1;                    //9、局部变量存在于栈上,nm无法查看int local2 = 3;foo();return 0;
}

把它编译成目标文件:

$ gcc -c -g main.c
使用nm查看目标文件:
$ nm -n main.o
$$ 00000000 T foo
$$ 00000000 D global2
$$ 00000000 R global3
$$ 00000000 b static_global1
$$ 00000004 C global1
$$ 00000004 b internal1.1555
$$ 00000004 d static_global2
$$ 00000005 t bar
$$ 00000008 d internal2.1556
$$ 0000000a T main
由于程序没有链接,此时列出的地址为相对偏移地址。另外, _time 符号在文件中没有定义,是因为它的实现位于 C 标准库中。链接过后再次查看,地址就会变成具体的地址了。

4、objdump,信息查看器

在嵌入式软件开发中,有时需要知道所生成的程序文件中的段信息以分析问题,或者需要查看 C 语言所对应的汇编代码,此时objdump 就派上用场了。
使用如下命令可以查看段信息:
$ objdump -h test
-d 选项可以显示程序文件的汇编代码:
$ objdump -d test
-d 查看汇编代码时结合 -S 可以显示汇编代码对应的 C/C++ 源程序(此时需要编译程序的时候不要使用优化选项):
$ objdump -S -d test
-f 选项可以显示程序文件的头信息:
$ objdump -f test
-s -j 配合使用可以查看某个段的具体内容:
$ objdump -s -j .data test

5、objcopy,段剪辑器

objdump 可以对段进行过滤剪辑。
可以通过 -j 选项指定需要剪辑的段(这也是嵌入式中最重要的功能,在有的嵌入式系统中,比如制作引导加载器时就需要用到 objcopy,以便将代码段抽取出来,然后将其烧写到系统的启动运行地址处):
$ objcopy -j .text test onlytext
剪辑多个段:
$ objcopy -j .text -j .data -j .bss test onlytext
与 -j 选项相反, -R 选项可以删除某些段:
$ objcopy -R .text test notext
--strip-debug 选项可以删除程序中的调试信息:
$ objcopy --strip-debug notext

6、ranlib,库索引生成器

在档案文件中生成文件索引信息,如二中提到的 ar s 参数。比如:
$ ranlib libmy.a

7、size,段大小观察器

可以用它查看程序文件各个段的大小:
$ size test  (-A 可以查看详细信息)

8、strings,字符串窥视器

strings 可以查看程序文件中的可显示字符,比如打印的字符串信息、函数名等。

9、strip,程序文件瘦身器

去除程序文件中的调试信息以便减少程序文件的大小。它的功能与objcopy --strip-debug一样。

binutils工具集相关推荐

  1. binutils工具集用法

    addr2line用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号. 在不少嵌入式开发环境中,编译器的名称往往不是gcc,而是想arm-rtems-gcc这样的,对于这种命名形式的编译器 ...

  2. Binutils工具集 GCC工具集介绍

    GNU Binutils详解:http://www.crifan.com/files/doc/docbook/binutils_intro/release/html/binutils_intro.ht ...

  3. Linux二进制实用工具Binutils工具集解析()

    From:http://blog.csdn.net/zqixiao_09/article/details/50783007 GNU Binutils:http://www.gnu.org/softwa ...

  4. 嵌入式软件开发杂谈(4):Binutils工具集

    文章目录 1 addr2line 2 nm 3 readelf 3.1 -h 3.2 -l 3.3 -S 3.4 -t 3.5 -s 3.6 -n 3.7 -d 4 size 5 objdump 6 ...

  5. Binutils工具集 解析

    对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高.目前,可以说嵌入式系统的开发工具是GNU的天下,因为来自GNU的GCC编译器支持大量的目标处理器.除了GCC,还有一个非常 ...

  6. GNU工具链简介(GCC编译器+Binutils工具集+GDB调试器+make工具)

    以下内容源于网络资源的学习与整理,如有侵权请告知删除. 一.什么是工具链 在软件工程中,工具链(toolchain)是一系列用于制作软件的工具.它包括用来编辑源代码的文本编辑器.生成可执行文件的编译器 ...

  7. GNU binutils工具集

    前言 GNU binutils工具提供了一系列二进制工具,用于日常开发过程中对程序调试和维护,经常会用到的工具如下: as:汇编编译器,用于将汇编代码编译成目标文件; addr2line:用于得到程序 ...

  8. binutils工具集之---nm

    nm用于列出程序文件中的符号.建立nmtest.c文件: 1 #include<time.h>2 3 int global1;4 int global2=3;5 6 static int ...

  9. binutils工具集——ld的用法

    以下内容源于网络资源的学习与整理,如有侵权请告知删除. 参考内容 https://www.cnblogs.com/huqingyu/archive/2005/02/28/110468.html 一.工 ...

最新文章

  1. oracle11g导出表时会发现少表,空表导不出解决方案。
  2. 3.6 交并比-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  3. linux mysql编译安装mysql_【MySQL安装】Linux下安装MySQL(预编译)
  4. LeakCanary,检测安卓,java内存泄漏
  5. 4由通道检测_博唐平四通道糖化血红蛋白检测仪通过上海临检中心性能验证(二)...
  6. Python学习---入门导学篇
  7. js原生实现贪吃蛇(解决连续键盘事件)
  8. 浅谈算法和数据结构: 一 栈和队列
  9. 【老兵不朽】时隔1年,jQuery 发布新版 3.4.0
  10. ZigBee 协议栈的调度顺序代码分析
  11. 根据车辆型号自动生成车辆编号
  12. 269个JavaScript工具函数,助你提升工作效率(2)
  13. HYSPLIT 教程 有关记录
  14. PNAS:杏仁核个体特异性功能连接:精准精神病学基础
  15. 美容院前台收银软件用什么好?
  16. 4399游戏存档的那些事儿
  17. 第九讲:一元函数积分学的几何应用
  18. 限制guest账号访问硬盘权限
  19. 薪酬管理助力国有企业数字化人力攻略布局
  20. 判断iphone4 retain屏幕方法

热门文章

  1. selenium+phantomjs 模拟抓取淘宝分类信息
  2. 网盘程序 ZFile安装
  3. 系统分析与设计(一)
  4. SRPG游戏开发(五十二)第十一章 地图动作与地图事件 - 一 初始化地图动作(Initialize Map Action)
  5. java报表数据可视化
  6. 电脑账户头像怎么删掉_win10系统账户头像如何删除?windows10账户头像清除方法...
  7. 【Hydro】龙格-库塔方法的公式推导
  8. Rust 不同方式创建Arc对象性能对比
  9. 【文献2014】固体火箭发动机中的流固耦合仿真
  10. 十进制转十六进制(python)