什么是GNU
GNU计划,又称革奴计划。是由Richard Stallman在1983年9月27日公开发起的。目标是创建一套完全免费、自由的操作系统,基本原则是源代码共享及思想共享。
所有在GNU计划下开发的软件均为GNU软件。
为保证GNU软件可以自由地“使用、复制、修改和发布”,所有GNU软件都在一份在禁止其他人添加任何限制的情况下授权所有权利给任何人的协议条款,GNU通用公共许可证(GNU General Public License,GPL)。
GPL条款下发布的一些主要的GNU项目软件有:
GCC:GNU编译器集,包括GNU C编译器。
G++:C++编译器,是GCC的一部分。
GDB:源代码级的调试器。
GNU make:UNIX make命令的免费版本。
GNU Emacs:文本编辑器及环境。
bash:命令解释器(shell)。
本地程序开发(PC--X86)
GNU工具:
/usr/bin/{gcc、g++、as、ld等}
库文件:
/usr/lib/lib*.so
头文件:
/usr/include
交叉开发(ARM)
GNU工具:
/usr/local/arm/arm920t/bin/
arm-angstrom-linux-gnueabi-gcc
arm-angstrom-linux-gnueabi-as
库文件:
/usr/local/arm/arm920t/arm-angstrom-linux-gnueabi/lib/
头文件:
/usr/local/arm/arm920t/arm-angstrom-linux-gnueabi/include
Linux环境下开发C/C++程序的工具主要是GNU工具,GNU工具集包括:
编译工具:把一个源程序编译为一个可执行程序
调试工具:能对执行程序进行源码或汇编级调试
软件工程工具:用于协助多人开发或大型软件项目的管理,如 make、CVS、Subvision
其他工具:用于把多个目标文件链接成可执行文件的链接器,或者用作格式转换的工具。
cpp:
进行预处理,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析;
cc1:
进行编译,生成.s为后缀的汇编文件;
as:
进行汇编,汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件;
ld:
进行链接,所有的目标文件被安排在可执行程序中的恰当的位置。同时,该程序所调用到的库函数也从各自所在的档案库中链接到合适的地方。
GNU二进制工具
addr2line:把程序地址转换为文件名和行号
ar:建立、修改、提取归档文件
as:主要用来编译GNU C编译器gcc输出的汇编文件,产生的目标文件由链接器ld连接。
c++filt:连接器使用它来过滤C++ 和Java 符号,防止重载函数冲突。
ld:GNU链接器。
nm:列出目标文件中的符号。
objcopy:文件格式转换。
objdump:显示一个或者更多目标文件的信息,主要用来反编译。
ranlib:产生归档文件索引,并将其保存到这个归档文件中。在索引中列出了归档文件各成员所定义的可重分配目标文件。
readelf:显示elf格式可执行文件的信息。
size:列出目标文件每一段的大小以及总体的大小。默认情况下,对于每个目标文件或者一个归档文件中的每个模块只产生一行输出。
strings:打印某个文件的可打印字符串。默认情况下,只打印目标文件初始化和可加载段中的可打印字符;对于其它类型的文件它打印整个文件的可打印字符,这个程序对于了解非文本文件的内容很有帮助。
strip:丢弃目标文件中的全部或者特定符号,减小文件体积。
写一测试程序:
#include<stdio.h>
int main()
{
return 0;
}
readelf readelf可以显示elf格式可执行文件的信息。ELF格式是UNIX系统实验室作为应用程序二进制接口开发的。ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一。
查看可执行文件“test”的头信息
[root@localhost testGNU]# readelf -h test
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048280
Start of program headers: 52 (bytes into file)
Start of section headers: 1836 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 25
size 小以及总体的大小。
size有两种输出格式,一种为“sysv”,另一种为“berkeley”,默认为berkeley的格式。第一种格式可以用“-A”或者“--format=sysv”指定,第二种格式用“-B”或“--format=berkeley”指定:
$ size test –A
$ size test -B
[root@localhost testGNU]# size test
text data bss dec hex filename
803 248 8 1059 423 test
[root@localhost testGNU]# size test -A
test :
section size addr
.interp 19 134512916
.note.ABI-tag 32 134512936
.gnu.hash 32 134512968
.dynsym 64 134513000
.dynstr 69 134513064
.gnu.version 8 134513134
.gnu.version_r 32 134513144
.rel.dyn 8 134513176
.rel.plt 16 134513184
.init 23 134513200
.plt 48 134513224
.text 408 134513280
.fini 28 134513688
.rodata 12 134513716
.eh_frame 4 134513728
.ctors 8 134517828
.dtors 8 134517836
.jcr 4 134517844
.dynamic 200 134517848
.got 4 134518048
.got.plt 20 134518052
.data 4 134518072
.bss 8 134518076
.comment 276 0
Total 1335
nm
nm可以列出目标文件中的符号。用法虽然简单,但是功能很强大。
nm可以列出的符号:
R 只读符号
N 调试符号
D 已经初始化的变量的符号
T Text段的符号。子程序都是这种符号
U 未定义的符号。例如文件中引用了不存在的函数
S 未初始化的符号,比如定义了一个全局变量int a;则a的符号就是这种类型
libsupc++ 提供支持C++语言的库函数
R 只读符号
nmdemo.c实例
#include<stdio.h>
static int i_a;
int i_b;
const c_c=3;
char *buf="Demo for study nm tools!";
extern int i_e;
void function_1()
{
printf("Any string you want!\n");
}
int num_add(int a1,int a2)
{
return a1+a2;
}
nm使用
$ gcc –g –O –c nmdemo.c
$ nm –A nmdemo.o
[root@localhost testGNU]# gcc -g -O -c nmdemo.c
[root@localhost testGNU]# ls
nmdemo.c nmdemo.c~ nmdemo.o test test.c test.i test.o test.s
[root@localhost testGNU]# nm -A nmdemo.o
nmdemo.o:00000000 D buf
nmdemo.o:00000000 R c_c
nmdemo.o:0000000b T function_1
nmdemo.o:00000004 C i_b
nmdemo.o:00000000 T num_add
nmdemo.o: U puts
nm使用
$ nm –Aa nmdemo.o
[root@localhost testGNU]# nm -Aa nmdemo.o
nmdemo.o:00000000 b .bss
nmdemo.o:00000000 n .comment
nmdemo.o:00000000 d .data
nmdemo.o:00000000 N .debug_abbrev
nmdemo.o:00000000 N .debug_aranges
nmdemo.o:00000000 N .debug_frame
nmdemo.o:00000000 N .debug_info
nmdemo.o:00000000 N .debug_line
nmdemo.o:00000000 N .debug_loc
nmdemo.o:00000000 N .debug_pubnames
nmdemo.o:00000000 N .debug_str
nmdemo.o:00000000 n .note.GNU-stack
nmdemo.o:00000000 r .rodata
nmdemo.o:00000000 r .rodata.str1.1
nmdemo.o:00000000 t .text
nmdemo.o:00000000 D buf
nmdemo.o:00000000 R c_c
nmdemo.o:0000000b T function_1
nmdemo.o:00000004 C i_b
nmdemo.o:00000000 a nmdemo.c
nmdemo.o:00000000 T num_add
nmdemo.o: U puts
nm使用
“a”选项会把调试符号也列出来。默认状态下调试符号不会被列出。
“l”选项会把源代码中对应的行号也列出
$ nm –Aal nmdemo.o
[root@localhost testGNU]# nm -Aal nmdemo.o
nmdemo.o:00000000 b .bss /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 n .comment /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 d .data
nmdemo.o:00000000 N .debug_abbrev /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_aranges /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_frame /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_info /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_line /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_loc /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_pubnames /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 N .debug_str /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 n .note.GNU-stack /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 r .rodata
nmdemo.o:00000000 r .rodata.str1.1
nmdemo.o:00000000 t .text /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o:00000000 D buf /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:5
nmdemo.o:00000000 R c_c /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:4
nmdemo.o:0000000b T function_1 /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:8
nmdemo.o:00000004 C i_b
nmdemo.o:00000000 a nmdemo.c
nmdemo.o:00000000 T num_add /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:12
nmdemo.o: U puts /mnt/hgfs/share/C++_C/testGNU/nmdemo.c:9
strip
strip用来丢弃目标文件中的全部或者特定符号,减小文件体积。对于嵌入式系统,这个命令必不可少。
[root@localhost testGNU]# vim nmdemo.c
[root@localhost testGNU]# ls -lh test
-rwxrwxrwx 1 root root 4.6K 12-08 13:11 test
strip -s 去除所有符号
[root@localhost testGNU]# strip -s test
[root@localhost testGNU]# ls -lh test
-rwxrwxrwx 1 root root 2.8K 12-08 13:11 test
strip使用
经过strip处理后的文件已经不包含符号了,可以使用nm加以验证。
[root@localhost testGNU]# nm nmdemo.o
00000000 D buf
00000000 R c_c
0000000b T function_1
00000004 C i_b
00000000 T num_add
U puts
[root@localhost testGNU]# strip nmdemo.o
[root@localhost testGNU]# nm nmdemo.o
nm: nmdemo.o: no symbols
strings
strings用来打印某个文件的可打印字符串。
[root@localhost testGNU]# strings nmdemo.o
Any string you want!
Demo for study nm tools!
如果想显示文件名称,使用“f”选项。
[root@localhost testGNU]# strings -f nmdemo.o
nmdemo.o: Any string you want!
nmdemo.o: Demo for study nm tools!
objdump
objdump可以显示一个或者更多目标文件的信息,主要用来反编译。
-d 反汇编
-i BFD文件支持信息
[root@localhost testGNU]# objdump -d nmdemo.o
nmdemo.o: file format elf32-i386
Disassembly of section .text:
00000000 <.text>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 0c mov 0xc(%ebp),%eax
6: 03 45 08 add 0x8(%ebp),%eax
9: 5d pop %ebp
a: c3 ret
b: 55 push %ebp
c: 89 e5 mov %esp,%ebp
e: 83 ec 08 sub $0x8,%esp
11: c7 04 24 00 00 00 00 movl $0x0,(%esp)
18: e8 fc ff ff ff call 0x19
1d: c9 leave
1e: c3 ret
“-R”选项显示动态重定向的入口。
objcopy
objcopy可以进行目标文件格式转换。
转成bin文件
[root@localhost testGNU]# objcopy -O binary test test.bin
[root@localhost testGNU]# ls
nmdemo.c nmdemo.c~ nmdemo.o test test.bin test.c test.i test.o test.s
addr2line
addr2line能够把程序地址转换为文件名和行号,前提是这个可执行文件包括调试符号。
如果可执行文件中没有包括调试符号,shell将返回??:0。
最常用的选项是“-e”用来指定文件名和地址。
08048384 T main
U printf@@GLIBC_2.0
[root@localhost testGNU]# addr2line 08048384 -e test
/mnt/hgfs/share/C++_C/testGNU/test.c:3