1. 程式人生 > >linux建立靜動態庫

linux建立靜動態庫

1.gcc編譯選項

-E : 預處理 .c -> .i
-S : 編譯 .i /.c -> .s
-c : 彙編 .s -> .o
-g : 生成除錯資訊
-O : 優化級別
-O0
-O1
-O2
-O3
-Os
-I : 包含一個頭檔案搜尋路徑 -I/home/linux/include
-L : 包含一個庫檔案搜尋路徑 -L/home/linux/lib
-l : 連線一個庫 , 庫以lib開頭, 以.so .a 結尾 , windows下以.dll

  1. gnu 二進位制工具集 之 readelf

readelf可以顯示elf格式可執行檔案的資訊
readelf -h test 檢視test可執行程式的頭資訊
ELF 頭:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
類別: ELF64
資料: 2 補碼,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
型別: EXEC (可執行檔案)
系統架構: Advanced Micro Devices X86-64
版本: 0x1
入口點地址: 0x4004b0
程式頭起點: 64 (bytes into file)
Start of section headers: 4520 (bytes into file)
標誌: 0x0
本頭的大小: 64 (位元組)
程式頭大小: 56 (位元組)
Number of program headers: 9
節頭大小: 64 (位元組)
節頭數量: 30
字串表索引節頭: 27

readelf -s test : 可以顯示 程式中的符號

59: 00000000004004b0 0 FUNC GLOBAL DEFAULT 13 _start 程式入口點

61: 000000000040059d 225 FUNC GLOBAL DEFAULT 13 main // c 程式開始執行的地方

readelf -S test : 可以顯示程式的段資訊
[13] .text PROGBITS 00000000004004b0 000004b0
0000000000000242 0000000000000000 AX 0 0 16
[15] .rodata PROGBITS 0000000000400700 00000700
000000000000001a 0000000000000000 A 0 0 4
[24] .data PROGBITS 0000000000601038 00001038
0000000000000010 0000000000000000 WA 0 0 8
[25] .bss NOBITS 0000000000601048 00001048
0000000000000008 0000000000000000 WA 0 0 1

  1. size 列出目標檔案每一段的大小以及總體的大小

size test : 預設輸出形式 bsd
text data bss dec hex filename
1527 568 8 2103 837 test
size test -A : 預設輸出形式 system v
test :
section size addr
.interp 28 4194872
.note.ABI-tag 32 4194900
.note.gnu.build-id 36 4194932
.gnu.hash 28 4194968
.dynsym 120 4195000
.dynstr 90 4195120
.gnu.version 10 4195210
.gnu.version_r 48 4195224
.rela.dyn 24 4195272
.rela.plt 96 4195296
.init 26 4195392
.plt 80 4195424
.text 578 4195504
.fini 9 4196084
.rodata 26 4196096
.eh_frame_hdr 52 4196124
.eh_frame 244 4196176
.init_array 8 6295056
.fini_array 8 6295064
.jcr 8 6295072
.dynamic 464 6295080
.got 8 6295544
.got.plt 56 6295552
.data 16 6295608
.bss 8 6295624
.comment 86 0
Total 2189

  1. 組合命令 nm 列出程式中的符號
    nm test : 列出程式中的符號
    0000000004006f0 T __libc_csu_fini
    0000000000400680 T __libc_csu_init
    000000000040059d T main
    0000000000400510 t register_tm_clones
    00000000004004b0 T _start
    0000000000601048 D TMC_END

ls -l test : 檢視程式的大小
strip test :對程式瘦身 , 縮減程式的尺寸
[email protected]:~/test$ ls -l test
-rwxrwxr-x 1 linux linux 8620 10月 25 09:35 test
[email protected]:~/test$ strip test
[email protected]:~/test$ ls -l test
-rwxrwxr-x 1 linux linux 6296 10月 25 10:03 test
[email protected]:~/test$ nm test
nm: test:無符號

  1. objdump 用來反向編譯
    objdump -d test >test.dis

  2. objcopy 格式轉換工具
    elf 轉bin
    objcopy -O binary test test.bin

  3. addr2line 根據地址找到地址所在的函式(符號)
    nm test
    000000000040059d T main

    addr2line 0x000000000040059d -e test -f
    main
    ??:? // 出現這個問題, 是編譯時沒有加 -g 選項

    重新生成test
    gcc test.c -o test -g
    nm test
    000000000040059d T main
    addr2line 0x000000000040059d -e test -f
    main
    /home/linux/test/test.c:3

  4. 製作庫檔案
    庫分為 : 靜態庫 libxxx.a
    動態庫 libxxx.so

    製作靜態庫流程:
    要製作庫的檔案中不能有main函式
    gcc -c print.c -o print.o
    ar -cr libprint.a print.o // 生成庫
    gcc test.c -o test -L. -lprint // 連線庫
    ./test

    製作動態庫的流程:
    gcc -o libprint.so print.c -fPIC -shared // 生成動態庫

    gcc test.c -o test -L. -lprint // 生成可執行程式

    sudo cp libprint.so /lib/ 把生成的動態庫複製到系統的庫路徑內, 程式執行時自動去系統路徑下去找動態庫

    ./test

    ./test: error while loading shared libraries: libprint.so: cannot open shared object file: No such file or directory
    解決方法: 把共享庫 xxx.so 複製到/lib
    第一個原始檔 my_print.c

#include <stdio.h>

void cout(const char * message)
{
    fprintf(stdout, "%s\n", message);
}

原始檔2: my_math.c

int add(int a, int b)
{
    return a + b;
}

int subtract(int a, int b)
{
    return a - b;
}

使用gcc,為這兩個原始檔生成目標檔案:

gcc -c my_print.c my_math.c
我們就得到了 my_print.o 和 my_math.o。

歸檔目標檔案,得到靜態庫。
我們使用 ar 將目標檔案歸檔:

ar crv libmylib.a my_print.o my_math.o
我們就得到了libmylib.a,這就是我們需要的靜態庫。

上述命令中 crv 是 ar的命令選項:

c 如果需要生成新的庫檔案,不要警告
r 代替庫中現有的檔案或者插入新的檔案
v 輸出詳細資訊
通過 ar t libmylib.a 可以檢視 libmylib.a 中包含的目標檔案。

可以通過 ar --help 檢視更多幫助。

注意:我們要生成的庫的檔名必須形如 libxxx.a ,這樣我們在連結這個庫時,就可以用 -lxxx。
反過來講,當我們告訴編譯器 -lxxx時,編譯器就會在指定的目錄中搜索 libxxx.a 或是 libxxx.so

生成對應的標頭檔案
標頭檔案定義了 libmylib.a 的介面,也就是告訴使用者怎麼使用 libmylib.a。

新建my_lib.h, 寫入內容如下:

#ifndef __MY_LIB_H__
#define __MY_LIB_H__

int add(int a, int b);
int subtract(int a, int b);

void cout(const char *);
#endif

測試我們的靜態庫
在同樣的目錄下,建立 test.c:


int main(int argc, char *argv[])
{
    int c = add(15, -21);
    cout("I am a func from mylib ...");
    return 0;
}

這個原始檔中引用了 libmylib.a 中的 cout 和 add 函式。

編譯test.c:

gcc test.c -L. -lmylib
將會生成a.out,通過 ./a.out 可以執行該程式。說明我們的靜態庫能正常工作。

上面的命令中 -L. 告訴 gcc 搜尋連結庫時包含當前路徑, -lmylib 告訴 gcc 生成可執行程式時要連結 libmylib.a。

通過makefile自動化
上面的步驟很繁瑣,還是寫個簡單的makefile吧,內容如下:

.PHONY: build test

build: libmylib.a

libmylib.a: my_math.o my_print.o
ar crv [email protected] my_math.o my_print.o

my_math.o: my_math.c
gcc -c my_math.c

my_print.o: my_print.c
gcc -c my_print.c

test: a.out

a.out: test.c
gcc test.c -L. -lmylib
makefile寫好後,執行 make build 將會構建 libmylib.a, 執行 make test 將會生成連結 libmylib.a 的程式。
***建立並使用動態庫
  第一步:編輯原始檔,test.h test.c main.c。其中main.c檔案中包含main函式,作為程式入口;test.c中包含main函式中需要用到的函式。
  vi test.h test.c main.c
  第二步:將test.c編譯成目標檔案。
  gcc -c test.c
  前面兩步與建立靜態庫一致。
  第三步:由。o檔案建立動態庫檔案。
  gcc -shared -fPIC -o libtest.so test.o
  第四步:在程式中使用動態庫。
  gcc -o main main.c -L. -ltest
  當靜態庫和動態庫同名時,gcc命令將優先使用動態庫。
  第五步:執行。
  LD_LIBRARY_PATH=. ./main
動態庫除了在預設的的路徑/lib 和 /usr/lib 下還可以通過"在配置檔案/etc/ld.so.conf中指定動態庫搜尋路徑",“環境變數的方式”和“在編譯目的碼時指定該程式的動態庫搜尋路徑(通過gcc 的引數"-Wl,-rpath,"指定)”三種,他們的優先順序也不一樣;

如果你在 windows 上使用 mingw,和Linux下生成靜態庫的方法是一樣的。
領卓教育