GNU-ARM程式開發(一)----GNU開發環境和工具
什麼是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”的頭資訊
[[email protected] 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
[[email protected] testGNU]# size test
text data bss dec hex filename
803 248 8 1059 423 test
[[email protected] 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
[[email protected] testGNU]# gcc -g -O -c nmdemo.c
[[email protected] testGNU]# ls
nmdemo.c nmdemo.c~ nmdemo.o test test.c test.i test.o test.s
[[email protected] 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
[[email protected] 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
[[email protected] 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用來丟棄目標檔案中的全部或者特定符號,減小檔案體積。對於嵌入式系統,這個命令必不可少。
[[email protected] testGNU]# vim nmdemo.c
[[email protected] testGNU]# ls -lh test
-rwxrwxrwx 1 root root 4.6K 12-08 13:11 test
strip -s 去除所有符號
[[email protected] testGNU]# strip -s test
[[email protected] testGNU]# ls -lh test
-rwxrwxrwx 1 root root 2.8K 12-08 13:11 test
strip使用
經過strip處理後的檔案已經不包含符號了,可以使用nm加以驗證。
[[email protected] 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
[[email protected] testGNU]# strip nmdemo.o
[[email protected] testGNU]# nm nmdemo.o
nm: nmdemo.o: no symbols
strings
strings用來列印某個檔案的可列印字串。
[[email protected] testGNU]# strings nmdemo.o
Any string you want!
Demo for study nm tools!
如果想顯示檔名稱,使用“f”選項。
[[email protected] testGNU]# strings -f nmdemo.o
nmdemo.o: Any string you want!
nmdemo.o: Demo for study nm tools!
objdump
objdump可以顯示一個或者更多目標檔案的資訊,主要用來反編譯。
-d 反彙編
-i BFD檔案支援資訊
[[email protected] 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檔案
[[email protected] testGNU]# objcopy -O binary test test.bin
[[email protected] 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 [email protected]@GLIBC_2.0
[[email protected] testGNU]# addr2line 08048384 -e test
/mnt/hgfs/share/C++_C/testGNU/test.c:3