GCC编译器原理(一)------GCC 工具:addr2line、ar、as、c++filt和elfedit
1.3 GCC 工具
1.3.1 binutils 工具集
工具 |
描述 |
addr2line |
给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻译成源代码文件名和行号。 |
ar |
这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通常使用该工具是为了创建和管理连接程序使用的目标库文档。 |
as |
GNU 汇编器。实际上它是一族汇编器,因为它可以被编译或能够在各种不同平台上工作。 |
c++filt |
程序接受被 C++编译程序转换过的名字(不是被重载的) ,而且将该名字翻译成初始形式。 |
elfedit |
更新 ELF 文件的 ELF 头。 |
gprof |
该程序会监督编译程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供的配置文件来优化程序。 |
ld |
GNU 连接程序。该程序将目标文件的集合组合成可执行程序。 |
ld.bfd |
到 ld 的硬链接。 |
libbfd |
二进制文件描述器库。该程序是 binutils 包的一部分 |
libiberty |
包含多个 GNU 程序会使用的途径,包括 getopt、obstack、strerror、strtol 和 strtoul。 |
libopcodes |
一个库,用于处理 opcodes——处理器指令的 "可读文本" 版本;用于编制 objdump 这样的工具。 |
nlmconv |
将可重定位的目标文件转换成 NetWare 可加载模块(NetWare Loadable Module,NLM) 。 |
nm |
列出目标文件中定义的符号。 |
objcopy |
将目标文件从一种二进制格式复制和翻译到另外一种。 |
objdump |
显示一个或多个目标文件中保存的多种不同信息。 |
ranlib |
创建和添加到 ar 文档的索引。该索引被 ld 使用来定位库中的模块。 |
readelf |
从 ELF 格式的目标文件显示信息 |
size |
列出目标文件中每个部分的名字和尺寸。 |
strings |
浏览所有类型的文件,析取出用于显示的字符串。 |
strip |
从目标文件或文档库中去掉符号表,以及其他调试所需的信息。 |
windres |
Window 资源文件编译程序。 |
1.3.2 addr2line:地址翻译工具
addr2line用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号。
参照对应工具的man和info信息,可以查找到详细信息。或者运行相应的工具并指定--help参数,可以获得该工具的简单帮助信息。
用法:addr2line [选项] [地址]
将地址转换成文件名/行号对。
如果没有在命令行中给出地址,就从标准输入中读取它们
选项是:@<file> 读取选项从 <file>
选项 |
描述 |
-a |
--addresses 显示地址 |
-b |
--target=<bfdname> 设置二进位文件格式 |
-e |
--exe=<executable><name> 设置输入文件名称(默认为 a.out) |
-i |
--inlines 解开内联函数 |
-j |
--section=<name> 读取相对于段的偏移而非地址 |
-p |
--pretty-print 让输出对人类更可读 |
-s |
--basenames 去除目录名 |
-f |
--functions 显示函数名 |
-C |
--demangle[=style] 解码函数名 |
-h |
--help 显示本帮助 |
测试代码:main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <stdio.h> #include <stdlib.h>
void func(void) { printf("func() address is %p\n", func); }
int main(int argc, char *argv[]) { func(); return 0; } |
编译:gcc -g main.c -o main -Wall
执行:./main
根据地址找到对应的代码位置
1.3.3 ar:创建和管理连接程序使用的目标库文档
ar 命令的语法如下:ar [options] [positionname] [count] archive objectfile [objectfile ...]
- options:选项标签,选项的字母表示之间没有空格,选项可通过(也可不通过)连字符来表达。
- 可选的命令行项目 positionname 和 count 只有当用到了某些需要它们的选项时才会出现。
- ar 命令的选项分为两类:命令行选项告诉 ar 应该采取什么样的操作(有效命令行选项中的一个) ,以及修改子(modifier) 选项指出如何执行该命令操作
指明操作的ar 选项:
选项 |
描述 |
d |
从文档中删除模块 objectfiles。利用 v 修改子,列出的所有模块都要被删除 |
m |
将模块移入文档内部。默认情况下,列举的所有 objectfiles 成员都会移到文档尾部。利用修改子 a、b 和 i 可将指定模块移到其他位置 |
p |
将指定的目标文件 objectfiles 的二进制内容打印到标准输出上。如果没有指定目标文件objectfiles,就打印所有目标文件。修改子 v 会打印所有列在其内容之前的名字 |
q |
将指定目标文件 objectfiles 快速附加到文档末尾,而不检查是否可以取代而不是一味添加。不会更新索引,因此 ranlib 必须在连接库之前使用 |
r |
将指定目标文件 objectfiles 插入文档。如果指定的所有目标文件都已经在文档中了,就用新的替换老的。如果指定文档不存在,就会创建此文档。默认条件下,新的模块会添加到文件的末尾,但是修改子 a、b 或 i 可用来指定新模块的位置 |
t |
显示一列文档文件的内容。修改子 v 会为该列表加上时间戳、所有者、组、以及每个模块的大小。如果没有指定目标文件 objectfiles,就会列出整个文档 |
x |
将指定目标文件展开成正常的磁盘文件。如果没有指定目标文件,就会展开所有文件 |
修改子选项:
选项 |
描述 |
a |
将新文件添加到命令行指定文件位置之后,该文件是作为位置文件的 |
b |
将新文件添加到命令行指定文件位置之前,该文件是作为位置文件的。这和选项 i 一样 |
c |
如果必要就创建文档。只要需要总会创建新的文档,但使用该选项会禁止警告消息 |
f |
截断文档内的文件名。通常 ar 允许任意长度的文件名,可是这样会导致文档的创建过程在某些系统中不能相互兼容 |
i |
立即将所有新文件添加到命令行指定的文件位置之前。这和选项 b 一样 |
N |
当文档中存在多个指定的目标文件时,count 参数可用来当作指定目标文件的选择子(selector) |
o |
当文件是从文档中展开时,原始日期可以得到保留 |
s |
即使没有改动文档,也会创建新的文档索引。在 ar 中会单独使用修改子,这和使用 ranlib 的结果一样 |
u |
当文件加入文档时,该选项说明只有比文档中现有文件更新的文件才会被添加。该修改子只有和选项 r 在一起才有效 |
v |
按照详细模式运行,在运行处理过程中显示附加信息 |
V |
显示版本信息,然后退出 |
1.3.4 as:GNU汇编器
它是 GNU 汇编器,主要用来编译 GNU C 编译器 gcc 输出的汇编文件,它将汇编代码转换成二进制代码,并存放到一个 object 文件中,该目标文件将由连接器 ld 连接
-
as的内部预处理主要包括三个方面的工作
- 调整和去除额外的间隔符,保留每行的关键字前的一个空格或者TAB,其他任意的间隔符都转换为一个空格。
- 去除所有注释,代之以一个空格,或者新行的合适的数字。
- 把字符常量转换成相应的数字值。
- 它不能做宏处理和文件包含处理,如果需要用,可以交给 C 预处理器来处理
语法:as(选项)(参数)
常用选项如下:
选项 |
描述 |
-ac |
忽略失败条件; |
-ad |
忽略调试指令; |
-ah |
包括高级源; |
-al |
包括装配; |
-am |
包括宏扩展; |
-an |
忽略形式处理; |
-as |
包括符号; |
=file |
设置列出文件的名字; |
--alternate |
以交互宏模式开始; |
-f |
跳过空白和注释预处理; |
-g |
产生调试信息; |
-J |
对于有符号溢出不显示警告信息; |
-L |
在符号表中保留本地符号; |
-o |
指定要生成的目标文件; |
--statistics |
打印汇编所用的最大空间和总时间。 |
例子:as -o main.o main.s –32,编译32位的汇编代码
1.3.5 c++filt:修复 C++ 和 Java 符号
主要是为了防止 C++ 和 Java 中的多个函数名重复产生的重载问题。由于每个重载函数都使用与原函数相同的名称,因此,支持函数重载的语言必须拥有一种机制,以区分同一个函数的许多重载版本。
c++filt 将每个输入的名称看成是改编后的名称,并设法确定用于生成该名称的编译器。
如果这个名称是一个合法的改编名称,那么,c++filt 就输出改编之前的名称; 如果c++filt无法识别一个改编名称,那它就按原样输出该名称。
不关注除 C 语言之外的语言,命令选项查看 man 手册
1.3.6 elfedit:检查或编辑 ELF 文件
elfedit 是一个用于检查或修改现有 ELF 目标文件内容的工具。可以访问目标文件中包含的大多数 ELF 数据,这些数据包括 ELF 头、节头表、程序头表、动态节、硬件和软件功能、字符串表和符号表。
elfedit 可以处理来自命令行(–e 选项)或标准输入的命令。如果标准输入是一个终端,elfedit 可提供终端编辑功能以及涵盖大量命令的命令补齐功能。ELF 对特殊整数值和位掩码使用许多标准符号名称。elfedit 可识别此类名称的最有可能的完整形式。在输入 elfedit 命令时,可以随时按 TAB 键,令 elfedit 显示用法消息以及当前光标处文本的任意已知完整形式。
elfedit 功能以模块形式组织。每个模块提供一组命令,这些命令针对相关功能。通过使用冒号 (:) 分隔符将模块和命令名称组合到一起(中间无空格)来指定命令。例如,dyn:runpath 指的是由 dyn 模块提供的 runpath 命令。模块名称必须是唯一的。给定模块中的命令名称在该模块中必须唯一,但可在多个模块中使用相同的命令名称。
某些模块将模块内的某个命令指定为该模块的缺省命令。用户只需指定模块名称,就可运行此命令。大多数 elfedit 模块均提供一个名为 dump 的命令,它针对模块涵盖的 ELF 文件部分生成的信息与 elfdump 实用程序所显示的信息相同。通常,模块会将 dump 指定为其缺省命令。
用于执行 elfedit 命令的语法在设计上采用类似 UNIX 命令行实用程序的语法,这样任何会使用 UNIX 命令行实用程序的人都可以方便地使用 elfedit 命令。该语法由空格分隔的标记组成:第一个标记是命令名称。选项(即以连字符 (-) 开头的参数)跟在命令后面。纯参数(操作数)跟在选项后面。一个给定的命令可以有 0 个或多个选项和操作数,但是如果它们同时存在,选项始终位于纯参数前面。可使用特殊选项 --(两个连字符)来限定选项的结尾。如果遇到此选项,其余所有参数均被视为纯参数,即使它们以 - 开头。
elfedit 标记中的字符的解释取决于所用的引用格式:
引用格式 |
描述 |
非引用 |
单引号 (') 或双引号 (") 外面的反斜杠 (\) 充当转义符。elfedit 发现反斜杠字符时会将其忽略,并按字面意思处理反斜杠后面的字符(即使它后面的字符为反斜杠)。此功能可用于在命令的字符串参数中插入一个空白字符,从而无需将一个字符串分为两个单独的标记。同样,它可用于插入一个引号或反斜杠作为文本字符。 |
单引号 |
在单引号 (') 中,空白字符不用于分隔标记,且会被解释为标记内部的文本字符。双引号 (") 和反斜杠 (\) 字符会被解释为文本字符,无特殊意义。 |
双引号 |
在双引号 (") 中,空白字符不用于分隔标记。单引号字符会被解释为文本,不具有引用功能。反斜杠 (\) 是一个转义字符,在字符串文本中,其作用与 C 编程语言中反斜杠的作用类似: |
\a 警报(响铃) |
|
\b 退格键 |
|
\f 换页符 |
|
\n 换行符 |
|
\r 回车 |
|
\t 水平制表符 |
|
\v 垂直制表符 |
|
\\ 反斜杠 |
|
\' 单引号 |
|
\" 双引号 |
|
\ooo 八进制常量,其中 ooo 是 1 到 3 个八进制位 (0...7) |
|
在反斜杠后面跟有任何其他字符,均会出错。 |
核心命令均属于一个名为 sys 的内部模块。所有其他模块均打包为可动态装入的可共享目标文件。当执行需要某个模块的命令时或者当执行 sys:load 命令时,elfedit 会按需装入模块。由于 sys 模块特殊的内置状态,而且其命令使用频繁,所以 elfedit 命令允许在不加 sys: 前缀的情况下指定 sys 模块中的命令,例如,使用 load 而非 sys:load。要访问任何其他模块中的命令,必须采用 module:cmd 完整格式指定。
elfedit 随以下标准模块一起提供:
模块 |
描述 |
cap |
功能节 |
dyn |
动态节 |
ehdr |
ELF 头 |
reloc |
重定位节 |
phdr |
程序头数组 |
shdr |
节头数组 |
str |
字符串表节 |
sym |
符号表节 |
syminfo |
Syminfo 节 |
sys |
内置的核心 elfedit 命令 |
命令用法:elfedit [-adr] [-e cmd] [-L path] [-o default | simple | num] [infile] [outfile]
选项 |
描述 |
–a |
启用 autoprint 模式。启用 autoprint 后,elfedit 将输出修改 ELF 文件后所生成的修改值。此输出以当前输出样式显示,可使用 –o 选项更改此样式。缺省输出样式是 elfdump(1) 实用程序使用的样式。以交互方式使用 elfedit 时,autoprint 模式为缺省模式(当 stdin 和 stdout 为终端时)。因此,仅当在非交互式上下文中使用 elfedit 时,–a 选项才有意义。要在交互式会话中禁用 autoprint,请使用 elfedit 命令:> set a off |
–d |
如果已设置,此选项可使 elfedit 发布信息性消息,说明其内部操作和要处理的 ELF 目标文件的详细信息。这在需要深入了解所执行的操作时非常有用。 |
–e cmd |
指定一个编辑命令。可以指定多个 –e 选项。如果在命令行上指定了多个编辑命令,elfedit 将在批处理模式下运行。打开文件后,elfedit 按给定的顺序执行每个命令,然后保存修改的文件,最后 elfedit 退出。从 shell 脚本和 makefile 执行简单操作时,批处理模式非常有用。 |
–L path |
设置用于定位 elfedit 模块的缺省路径。本手册页的"模块搜索路径"部分介绍了各个模块。 |
–o default | simple | num |
用于显示 ELF 数据的样式。此选项用于确立会话的当前样式。可在 elfedit 会话中更改此样式,方法是使用 set (sys:set) 命令或向会话中执行的各个命令提供 –o 选项。 |
default |
缺省样式是以适合用户查看的格式显示输出。此样式与 elfdump 实用程序使用的样式类似。 |
num |
整数值始终以整数格式显示。字符串显示为在内含的字符串表中定位所用的整数偏移量。 |
simple |
显示 ELF 文件中的字符串时,仅显示字符串。如果可能,整数值显示为符号常量,否则以整数格式显示。不显示任何标题、头或其他补充输出。 |
–r |
只读模式。输入文件以只读访问模式打开,编辑会话的结果不会保存。指定了 –r 时,elfedit 不允许使用 outfile 参数。如果不打算修改文件,强烈建议使用只读模式。除了提供额外保护以防止意外修改外,该选项还允许检查用户没有写入权限的文件。 |
操作数支持:
操作数 |
描述 |
infile |
包含要处理的 ELF 目标文件的输入文件。 此文件可以为可执行文件 (ET_EXEC)、共享目标文件 (ET_DYN) 或可重定位目标文件 (ET_REL)。不直接支持归档文件。要编辑归档中的目标文件,必须提取目标文件,编辑副本,然后将其重新插入到该归档文件。 如果未提供 infile,elfedit 将在限定模式下运行,此模式仅允许执行 sys: 模块中的命令。此模式主要用于访问 help (sys:help) 命令提供的命令文档。 如果提供了 infile,但未给定任何 outfile,elfedit 将就地编辑文件并将结果写入同一文件,这会导致原始文件内容被覆盖。通常,不建议以此模式使用 elfedit,建议指定输出文件。生成的文件经过测试和验证后,可将其移动到原始文件所在的位置。 –r 选项可用于以只读访问模式打开 infile。在检查不希望修改的现有文件时,此选项非常有用。 |
outfile |
输出文件。如果同时提供了 infile 和 outfile,infile 将以只读访问模式打开,修改的目标文件内容会写入到 outfile 中。 |