linux下gcc編譯生成靜態及動態庫

在Linux下如何使用GCC編譯程序、簡單生成 靜態庫及動態庫。

本文適用於Linux下開發初學者。本文初步講解在Linux下如何使用GCC編譯程序、簡單生成靜態庫及動態庫。

一、關於安裝。一般系統默認是安裝好編譯器的,並且網絡上有大量資料介紹不同發行版本下的安裝問題,本文不再描述。

二、C編程中的文件後綴名介紹
.a 靜態庫(打包文件)
.c 未經過預處理的C源碼
.h C頭文件
.i 經過預處理的C源碼
.o 編譯之後產生的目標文件
.s 生成的彙編語言代碼
.so 動態庫(動態鏈接庫)
解釋:*.a是我們在編譯過後用ar打包生成的靜態庫;*.c一般使我們自己編輯的代碼,使我們勞動的結晶;*.h一般是 我們手工生成的接口文件,如果願意,也可在*.c完成後用GCC的選項-aux-info幫我們生成;*.i是經過預處理後的源碼,是由GCC在選項-E編譯下自動生成 的文件;*.o是編 譯後產生的目標文件;*.s是GCC在選項-S編譯下生成的彙編語言代碼,對於性能要求很高的程序可以先生成彙編語言文件並對彙編做優化,然後用優 化後的彙編生成目標文件並鏈接;*.so是動態庫,通過GCC的-fpic -shared選項生成。

三、hello.c的編譯過程

本小節的演示都針對文件 hello.c 進行
  1. /*
  2. * hello.c
  3. */

  4. #include <stdio.h>
  5. int main()
  6. {
  7. printf("hello, world!/n");
  8. return 0;
  9. }
1.直接生成可執行程序
  1. $ gcc -o hello hello.c
  2. $ ./hello
  3. hello, world!

  4. 如 下編譯方式 結果相同:
  5. $ gcc hello.c -o hello
  6. $ ./hello
  7. hello, world!

  8. 如 下編譯方式 有別於以上編譯方 案(具體查找ELF和a.out文件格式差別的網絡資料,對於此處結果是無任何區別的):
  9. $ gcc hello.c
  10. $ ./a.out
  11. hello, world!

2.生成預處理後的文件 hello.i
  1. $ gcc -E hello.c -o hello.i
  2. $ ls
  3. a.out hello hello.c hello.i
  4. hello.i 就 是新生成的文件

  5. 如下語句結果相同:
  6. $ gcc -E -o hello.i hello.c

  7. 如 果不設定輸出文件,則打印到標準終端,此時我們可以用 less 查看:
  8. $ gcc -E hello.c | less
  9. # 1 "hello.c"
  10. # 1 "<built-in>"
  11. # 1 "<command line>"
  12. # 1 "hello.c"
  13. # 1 "/usr/include/stdio.h" 1 3 4
  14. # 28 "/usr/include/stdio.h" 3 4
  15. # 1 "/usr/include/features.h" 1 3 4
  16. # 329 "/usr/include/features.h" 3 4
  17. ..............................

  18. 或 者執行:
  19. $ gcc -E hello.c -o hello.i
  20. $ vi hello.i
  21. 1 # 1 "hello.c"
  22. 2 # 1 "<built-in>"
  23. 3 # 1 "<command line>"
  24. 4 # 1 "hello.c"
  25. 5 # 1 "/usr/include/stdio.h" 1 3 4
  26. 6 # 28 "/usr/include/stdio.h" 3 4
  27. 7 # 1 "/usr/include/features.h" 1 3 4
  28. 8 # 329 "/usr/include/features.h" 3 4

  29. .......... <中間部分略> ..................

  30. 929 # 844 "/usr/include/stdio.h" 3 4
  31. 930
  32. 931 # 2 "hello.c" 2
  33. 932
  34. 933 int main()
  35. 934 {
  36. 935 printf("hello, world!/n");
  37. 936
  38. 937 return 0;
  39. 938 }

  40. 可 見,將近1000行的代碼,我們的只佔了最末8行。

3.生成彙編語言文件 hello.s
  1. $ gcc -S hello.c -o hello.s
  2. $ ls
  3. a.out hello hello.c hello.i hello.s
  4. hello.s就是新生成的文件

  5. 如下語句結果相同:
  6. $ gcc -S -o hello.s hello.c

  7. 如 下語句結果相同:
  8. $ gcc -S hello.c

  9. 也 可以採用前一步驟產生的中間文件生成彙編文件:
  10. $ gcc -S hello.i -o hello.s
  11. $ gcc -S -o hello.s hello.i
  12. $ gcc -S hello.i


  13. 生 成的彙編部分代碼如下:
  14. $ vi hello.s
  15. 1 .file "hello.c"
  16. 2 .section .rodata
  17. 3 .LC0:
  18. 4 .string "hello, world!"
  19. 5 .text
  20. 6 .globl main
  21. 7 .type main, @function
  22. 8 main:
  23. 9 leal 4(%esp), %ecx
  24. 10 andl $-16, %esp
  25. 11 pushl -4(%ecx)
  26. 12 pushl %ebp
  27. // 註釋:如果你熟悉,就可以對部分彙編優化以達到更好效果。
4.生成目標文件 hello.o
  1. $ gcc -c hello.c -o hello.o
  2. $ ls
  3. a.out hello hello.c hello.i hello.o hello.s
  4. hello.o就是新生成的目標文件:

  5. 如下語句結果相同:
  6. $ gcc -c -o hello.o hello.c

  7. 如 下語句結果相同:
  8. $ gcc -c hello.c

  9. 也 可以採用前面步驟產生的中間文件hello.i或hello.s來生成目標文件:
  10. $ gcc -c hello.i
  11. $ gcc -c hello.s

  12. 我 們可以用 objdump 查看 hello.o 的二進制碼:
  13. $ objdump -s hello.o

  14. hello.o: file format elf32-i386

  15. Contents of section .text:
  16. 0000 8d4c2404 83e4f0ff 71fc5589 e55183ec .L$.....q.U..Q..
  17. 0010 04c70424 00000000 e8fcffff ffb80000 ...$............
  18. 0020 000083c4 04595d8d 61fcc3 .....Y].a..
  19. Contents of section .rodata:
  20. 0000 68656c6c 6f2c2077 6f726c64 2100 hello, world!.
  21. Contents of section .comment:
  22. 0000 00474343 3a202847 4e552920 342e312e .GCC: (GNU) 4.1.
  23. 0010 31203230 30373031 30352028 52656420 1 20070105 (Red
  24. 0020 48617420 342e312e 312d3532 2900 Hat 4.1.1-52).
5.採用中間級文件生成可執行程序
  1. $ gcc -o hello hello.i
  2. $ ./hello
  3. hello, world!

  4. $ gcc -o hello hello.s
  5. $ ./hello
  6. hello, world!

  7. $ gcc -o hello hello.o
  8. $ ./hello
  9. hello, world!
四、 靜態庫的生成
linux下靜態庫的生成比較方便。在生成目標文件後用 ar 打包即可。在中大型項目中一個模塊一般會做成一個靜態庫,以方便管理、提高編譯、鏈接效率。
本小節的展示針對 main.c、func1.c、func2.c三個文件
  1. /*
  2. * main.c
  3. */
  4. #include <stdio.h>

  5. extern int func1();
  6. extern int func2();

  7. int main()
  8. {
  9. int i;

  10. i = func1();
  11. printf("func1 return = %d/n",i);

  12. i = func2();
  13. printf("func2 return = %d/n",i);

  14. return 0;
  15. }

-----------------------------------------------------
  1. /*
  2. * func1.c
  3. */
  4. int func1()
  5. {
  6. return 100;
  7. }
-----------------------------------------------------
  1. /*
  2. * func2.c
  3. */
  4. int func2()
  5. {
  6. return 200;
  7. }
一 下是編譯指 令:
  1. $ gcc -c func1.c
  2. $ gcc -c func2.c
  3. $ ls
  4. func1.c func1.o func2.c func2.o main.c

  5. func1.o 和 func2.o 是 我們生成的目標文件。打包指令如下:
  6. $ ar -r libfunc.a func1.o func2.o

  7. 我 們查看 libfunc.a 中的文件:
  8. $ ar -t libfunc.a
  9. func1.o
  10. func2.o

  11. 現 在用靜態庫和 main.c 共同生成目標程序:
  12. $ gcc -o main main.c libfunc.a
  13. $ ./main
  14. func1 return = 100
  15. func2 return = 200

  16. 和 我們的預期相符合。下面我們進入動態庫。


五、動態庫的生成
linux下動態庫的生成通過GCC選項實現。案例程序和靜態庫中的相同。一下是操作指令:
  1. 首 先我們生成目標文件,但是需要加編譯器選項 -fpic 和鏈接器選項 -shared
  2. $ gcc -fpic -c func1.c
  3. $ gcc -fpic -c func2.c
  4. $ gcc -shared -o libfunc.so func1.o func2.o
  5. $ ls
  6. func1.c func1.o func2.c func2.o libfunc.so main.c

  7. libfunc.so就是我們生成的目標動態庫。我們用動態庫和 main.c 生成目標程序:
  8. $ gcc -o main main.c -L. -lfunc

  9. 注 意,我們用 -L. -lfunc 作爲編譯選項。-L. 表從當前目錄查找需要的動態庫,-lfunc 是動態庫的調用規則。Linux系統下的動態庫命名方 式是 lib*.so,而在鏈接時表示位 -l* , *是自己起的庫名。下面我們運行它:

  10. $ ./main
  11. ./main: error while loading shared libraries: libfunc.so: cannot open shared object file: No such file or directory

  12. 提 示一個錯誤, 指示無法找到動態庫。在linux下最方便的解決方案是拷貝libfunc.so到絕對目錄 /lib 下。但是隻有超級用戶纔有這個權限。另外一個方案 是更改環境變量 LD_LIBRARY_PATH。如下:
  13. $ $ export LD_LIBRARY_PATH=`pwd`
  14. $ ./main
  15. func1 return = 100
  16. func2 return = 200

  17. 運 行成功。現在我們更改動態庫的函數而不重新鏈接。如下:
  18. 更改 func1.c 爲:
  19. int func1()
  20. {
  21. return 101;
  22. }
  23. 更 改 func2.c 爲:
  24. int func2()
  25. {
  26. return 202;
  27. }
  28. 重 新生成庫:
  29. $ gcc -fpic -shared func1.c func2.c -o libfunc.so
  30. $ ./main
  31. func1 return = 101
  32. func2 return = 202

  33. 可 以看出,動態庫已經更新了。

六、結束語
本文簡單介紹了linux下如何使用gcc進行編譯程序、以及簡 單的靜態、動態庫的生成。靜態庫提供了一種打包管理方案,而動態庫使程序局部更新成爲了可能,更重要的是,當有多份實例存在時,動態庫可減小內存的消耗 (只佔用一份代碼空間)。
對本系列知識感興趣者可繼續跟蹤閱讀後續文章:庫的版本管理、GCC的編譯選項、Makefile與自動化編譯

 

 

本文轉自:http://chenyanxi.blog.51cto.com/4599355/928648

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章