一、GNU Binutils简介

GNU Binutils(GNU二进制工具集),即GNU Binary Utilities,是一套用于创建、管理和维护二进制目标文件的工具集合,包括addr2line、ar、gprof、nm、objcopy、objdump、ranlib、size、strings、strip。

Binutils官网地址:https://www.gnu.org/software/binutils/

二、GNU Binutils工具使用

1、addr2line

addr2line用于将可执行文件中代码地址转换为文件名和行号,可执行文件必须带调试信息。

addr2line [-a| --addresses ] [-b bfdname | --target=bfdname] [-C | --demangle[=style]] [-e filename | --exe=filename] [-f | --function] [-s | --basename] [-i | --inlines] [-p | --pretty-print] [-j | --section=name] [-H | --help] [-V | --version] [addr addr ...]

-a --addresses:在函数名、文件和行号信息前,显示地址,以十六进制形式。
-b --target=<bfdname>:指定目标文件格式为bfdname。
-e --exe=<executable>:指定需要转换地址的可执行文件名。
-i --inlines: 如果需要转换的地址是内联函数,则输出的信息包括其最近范围内的一个非内联函数的信息。
-j --section=<name>:给出的地址代表指定section的偏移,而非绝对地址。
-p --pretty-print:使得函数输出信息更加人性化:每一个地址的信息占一行。
-s --basenames:仅仅显示每个文件名的基址(即不显示文件的具体路径,只显示文件名)。
-f --functions:在显示文件名、行号输出信息的同时显示函数名信息。
-C --demangle[=style]:将低级别的符号名解码为用户级别的名字。
-h --help:输出帮助信息。
-v --version:输出版本号。

在程序执行过程中出现崩溃时,addr2line可用于快速定位出错位置,进而找出代码的bug。

 编译:

gcc -g test.c -o test

开启core dump:

ulimit -c unlimited

运行程序,当前目录下生产core文件:

./test

读取core文件,获取IP寄存器值

demsg core.733

 ip(指令指针寄存器)字段后的数字是test程序出错时程序执行的位置。

定位代码行:

addr2line -a 00000000004004fd -f -p -C -e test

 定位至test.c文件第7行。

2、ar

ar用于创建、修改静态库文件,从静态库中提取单个模块等。

ar [-]p[mod] [--plugin name] [--target bfdname] [relpos] [count] archive [member...]

操作模式:

d:从库中删除模块。按模块原来的文件名指定要删除的模块。

m:在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。可以使用'a','b',或'I'操作选项移动到指定的位置。
p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。
q:快速追加。增加新模块到库的结尾处,不会检查是否需要替换。
r:在库中插入模块(替换)。如果插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,不会替换 其它同名模块。
t:显示库的模块表清单。
x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。

操作选项:

a:在库的一个已经存在的成员后面增加一个新的文件。如果使用选项a,则应该为命令行中membername参数指定一个已经存在的成员名。
b:在库的一个已经存在的成员前面增加一个新的文件。如果使用选项b,则应该为命令行中membername参数指定一个已经存在的成员名。
c:创建一个静态库。不管库是否存在,都将创建。
f:在库中截短指定的名字。缺省情况下,文件名的长度是不受限制的,可以使用f参数将文件名截短,以保证与其它系统的兼容。
i:在库的一个已经存在的成员前面增加一个新的文件。如果使用选项i,则应该为命令行中membername参数指定一个已经存在的成员名。
N:与count参数一起使用,在库中有多个相同的文件名时指定提取或输出的个数。
o:当提取成员时,保留成员的原始数据。如果不指定o选项,则提取出的模块的时间将标为提取出的时间。
P:进行文件名匹配时使用全路径名。
s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。
S:不创建目标文件索引,在创建较大的库时能加快时间。
u:插入列出文件中比库中同名文件新的文件,只用于r操作选项。
v:用来显示执行操作选项的附加信息。
V:显示ar的版本.

ar crt libtest.a test.o

3、c++filt

C++编译器通过name mangling机制支持C++函数重载,因此经过C++编译器编译后的函数符号与C++函数名称并不一致。

c++filt是用于查找经过name mangling后的C++函数符号的原本函数名称的工具。

 4、nm

nm用于列出目标文件中的符号,输出结果包含地址、段、标识符。

nm [-A|-o|--print-file-name] [-a|--debug-syms]
          [-B|--format=bsd] [-C|--demangle[=style]]
          [-D|--dynamic] [-fformat|--format=format]
          [-g|--extern-only] [-h|--help]
          [-l|--line-numbers] [-n|-v|--numeric-sort]
          [-P|--portability] [-p|--no-sort]
          [-r|--reverse-sort] [-S|--print-size]
          [-s|--print-armap] [-t radix|--radix=radix]
          [-u|--undefined-only] [-V|--version]
          [-X 32_64] [--defined-only] [--no-demangle]
          [--plugin name] [--size-sort] [--special-syms]
          [--synthetic] [--target=bfdname]
          [objfile...]

-a或--debug-syms:显示调试符号。

-B:等同于--format=bsd,用来兼容MIPS的nm。

-C或--demangle:将低级符号名解码(demangle)成用户级名字,可以使得C++函数名具有可读性。

-D或--dynamic:显示动态符号。

-f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。

-g或--extern-only:仅显示外部符号。

-n、-v或--numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。

-p或--no-sort:按目标文件中遇到的符号顺序显示,不排序。

-P或--portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。

-s或--print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。

-r或--reverse-sort:反转排序的顺序(例如,升序变为降序)。

--size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。

-t radix或--radix=radix:使用radix进制显示符号值。radix只能为“d”表示十进制、“o”表示八进制或“x”表示十六进制。

--target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。

-u或--undefined-only:仅显示没有定义的符号(那些外部符号)。

-l或--line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。

-V或--version:显示nm的版本号。

 段标识符如下:

A:地址值在链接过程中不会发生改变

B/b:标识符位于未初始化数据段(.bss)

C:未定义存储段的标识符,链接时决定段位置

D/d:标识符位于数据段(.data)

N:调试专用标识符

R/r:标识符位于只读存储区(.rdata)

T/t:标识符位于代码段(.text)

U:未定义标识符

5、objcopy

objcopy用于从源目标文件拷贝内容到目标文件,用于不同格式的二进制文件的转换。

objcopy [option] infile [oufile]

infile:源目标文件

outfile:输出目标文件

-I bfdname,--input-target=bfdname:指定源目标文件格式,如果不指定,objcopy会自己分析源目标文件的格式

-O bfdname,--output-target=bfdname:指定输出目标文件格式

-S|--strip-all:不从源目标文件拷贝重定位和调试信息

-g|--strip-debug:不从源目标文件拷贝调试符号或分区信息

-K symbolname|--keep-symbol=symbolname:保留指定符号

-N symbolname|--strip-symbol=symbolname:移除指定符号

-x,--discard-all:不从源目标文件拷贝非全局符号

-R sectionpattern,--remove-section=sectionpattern:

--gap-fill=val:使用val填充段与段间的空闲区域

objcopy -O binary test test.bin

将文件转换成 rawbinary 格式

6、objdump

objdump用于查看目标文件或者可执行文件的构成。

参数选项如下:

-a,--archive-headers:显示档案库的成员信息。

-b bfdname,--target=bfdname:指定目标码格式。objdump -i可以列出可以指定的目标码格式列表。

-g,--debugging:显示调试信息。

-e,--debugging-tags:使用ctags格式显示调试信息。

-d,--disassemble:反汇编可执行文件section。

-D,--disassemble-all: 反汇编可执行文件所有section。

--prefix-addresses:反汇编的时候,显示每一行的完整地址。

-EB --endian=big:反汇编时指定大端格式。

-EL --endian=little:反汇编时指定小端格式。

-h,--section-headers --headers:显示目标文件各个section的头部摘要信息。

-H,--help:帮助信息。

-i,--info:显示-b或者-m选项可用的架构和目标格式列表。

-j name,--section=name:仅仅显示指定名称为name的section的信息

-m machine,--architecture=machine:指定反汇编目标文件时使用的架构,如果待反汇编文件本身没描述架构信息时需要指定。

-r,--reloc:显示文件的重定位入口。

-R,--dynamic-reloc:显示文件的动态重定位入口。

-s,--full-contents:显示指定section的完整内容。默认所有的非空section都会被显示。

-S,--source:反汇编出源代码。

--show-raw-insn:反汇编时,显示每条汇编指令对应的机器码,如不指定--prefix-addresses,是缺省选项。

--start-address=address:从指定地址开始显示数据。

--stop-address=address:显示数据直到指定地址为止。

-t,--syms:显示文件的符号表入口。

-T,--dynamic-syms:显示文件的动态符号表入口,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。

-V,--version:版本信息

-x,--all-headers:显示所可用的头信息,包括符号表、重定位入口。

-z,--disassemble-zeroes:一般反汇编输出将省略大块的零,本选项会将零块也被反汇编。

objdump -j .data -S test

 7、readelf

readelf用于显示ELF格式(目标)文件的信息。

-a:--all,显示全部信息,等价于 -h -l -S -s -r -d -V -A -I

-h:--file-header,显示elf文件开始的文件头信息

-S:--section-headers,显示节头信息。

-g:--section-groups,显示节组信息(如果有的话)。

-t:--section-details,显示节的详细信息(-S的)。

-s:--symbols,显示符号表段中的项(如果有的话)。

-e:--headers,显示全部头信息,等价于: -h -l -S

-n:--notes,显示note段(内核注释)的信息。

-r:--relocs,显示可重定位段的信息。

-u:--unwind,显示unwind段信息。当前只支持IA64 ELF的unwind段信息。

-d:--dynamic,显示动态段的信息。

-V:--version-info,显示版本段的信息。

-A:--arch-specific,显示CPU构架信息。

-D:--use-dynamic 使用动态段中的符号表显示符号,而不是使用符号段。

-x <number or name>:--hex-dump=<number or name> 以16进制方式显示指定段内内容。number指定段表中段的索引,或字符串指定文件中的段名。

-I:--histogram,显示符号的时候,显示bucket list长度的柱状图。

-v:--version,显示readelf的版本信息。

-H:--help,显示readelf所支持的命令行选项。

-W:--wide,宽行输出。

8、size

size显示目标文件或(.a)档案文件中所有section的大小。

size [-A|-B|--format=compatibility]
            [--help]
            [-d|-o|-x|--radix=number]
            [--common]
            [-t|--totals]
            [--target=bfdname] [-V|--version]
            [objfile...]
-A|-B|--format=compatibility

数据输出格式:-A等价于--format=sysv,-B等价于--format=berkeley。

-d|-o|-x|--radix=number

控制section显示的数据进制格式,-d等价于--radix=10,-o等价于--radix=8,-x等价于--radix=16。

  9、strings

strings用于显示目标文件中的(可打印)所有字符串信息。

-d,--data:只打印目标文件中的data分区中初始化字符串

 10、strip

strip用于从可执行文件中有选择地除去行号信息、重定位信息、调试段、typchk段、注释段、文件头以及所有或部分符号表,以实现在不影响程序功能的前提下,减少可执行文件的大小,减少程序的空间占用。

通常只在已经调试和测试过的生成模块上使用strip 命令,使用strip 命令减少对象文件所需的存储量开销。

为了方便定位问题(比如定位 core dump问题), 尽量不要strip,除非存储紧张。工程实践中,若需要对动态库.so进行strip操作,减少占地空间,通常使用strip前的库用来调试, strip后的库用于发布, 两者建立对应关系,一旦发布版出问题,可以找对应的未strip库来定位。

-s|--strip-all:删除所有符号信息

-S|-g|-d|--strip-debug:只删除调试符号信息

-K symbolname |--keep-symbol=symbolname:保留符号

-N symbolname |--strip-symbol=symbolname:移除符号

-o:输出文件

strip -K func test.o

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐