Linux下編譯生成SO並進行呼叫執行
參考部落格的部落格:
C編譯: 動態連線庫 (.so檔案) - Vamei - 部落格園 (cnblogs.com)
C 多個動態庫存在同名函式問題處理方法:-fvisibility=hidden_more_HH-CSDN部落格_fvisibility
Linux編譯動態連結庫so避免執行時才發現函式未定義符號的錯誤undefined symbol的ld引數 (gmd20.github.io)
檢視so庫的方法__臣本布衣_新浪部落格 (sina.com.cn)
Linux 動態庫同名函式處理-iibull-ChinaUnix部落格
1 程式碼結構
(1)include中是用於生成SO的標頭檔案,當前只有一個test.h檔案,內容如下:
void print_func(void);
(2)src中是用於生成SO的原始檔,當前只有一個test.c檔案,內容如下:
#include <stdio.h>
void print_func(void)
{
int i = 0;
for (; i < 10; i++)
printf("i = %d\n", i);
return;
}
(3)Makefile檔案是用於生成SO的,內容如下:
PROJECT=libprint_func.so
CC?=gcc
SOURCES=$(wildcard src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
.PHONY:clean
CFLAG = -Iinclude -fPIC -shared
LD_FLAG = -fPIC -s -Wl,-z,relro,-z,now,-z,noexecstack -fstack-protector-all
$(PROJECT): $(OBJECTS)
mkdir -p lib
$(CC) -shared -o lib/$@ $(patsubst %.o,obj/%.o,$(notdir $(OBJECTS))) $(LD_FLAG)
@echo "finish $(PROJECT)"
.c.o:
@mkdir -p obj
$(CC) -c $< $(CFLAG) -o obj/$(patsubst %.c,%.o,$(notdir $<))
clean:
-rm -rf obj lib
@echo "clean up"
生成的SO的名字為libprint_func.so。
(4)main.c用於測試,函式內部會呼叫test.c中的函式,程式碼如下:
#include <stdio.h>
#include "test.h"
int main(void)
{
print_func();
return 0;
}
2 編譯SO
編譯方式,直接在Makefile所在目錄執行make即可。
$ ls
include/ main.c Makefile src/
$ make
cc -c src/test.c -Iinclude -fPIC -shared -o obj/test.o
mkdir -p lib
cc -shared -o lib/libprint_func.so obj/test.o -fPIC -s -Wl,-z,relro,-z,now,-z,noexecstack -fstack-protector-all
finish libprint_func.so
執行了make之後,會在當前目錄生成兩個資料夾lib和obj,lib目錄存放SO檔案,obj目錄存在生成的obj檔案。
$ ls lib/libprint_func.so
lib/libprint_func.so*
$ ls obj/test.o
obj/test.o
3 生成可執行檔案
接著編譯main.c,生成可執行檔案進行測試。
編譯方式:
gcc -o test main.c -lprint_func -Llib -Iinclude -Wall
-l:說明庫檔案的名字,使用-lprint_func (即libprint_func庫檔案)
-L:指定編譯庫檔案所在位置
-I(大寫的i):指定標頭檔案所在檔案
-Wall:列印所有警告
注意:編譯時必須包括SO中函式的標頭檔案(test.h),否則會提示隱形宣告的警告。
例如:在main.c中註釋掉#include "test.h"。
#include <stdio.h>
//#include "test.h"
int main(void)
{
print_func();
return 0;
}
編譯時會提示警告:
$ gcc -o test main.c -lprint_func -Llib -Iinclude -Wall
main.c: In function ‘main’:
main.c:6:5: warning: implicit declaration of function ‘print_func’ [-Wimplicit-function-declaration]
print_func();
4 執行可執行檔案
直接執行可執行檔案,會提示找不到libprint_func.so檔案。
$ ./test
./test: error while loading shared libraries: libprint_func.so: cannot open shared object file: No such file or directory
可通過gcc -print-search-dirs命令來檢視庫的搜尋路徑:
$ gcc -print-search-dirs
install: /usr/lib/gcc/x86_64-redhat-linux/4.8.5/
programs: =/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/bin/
libraries: =/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/x86_64-redhat-linux/4.8.5/:/lib/../lib64/:/usr/lib/x86_64-redhat-linux/4.8.5/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/lib/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
可以通過ldd命令檢視可執行檔案依賴的庫:
$ ldd ./test
linux-vdso.so.1 => (0x00007ffd5b943000)
libprint_func.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007efec2afc000)
/lib64/ld-linux-x86-64.so.2 (0x00005593b3bba000)
可以看到找不到libprint_func.so庫,有兩種方法可以解決。
方法一:將libprint_func.so拷貝到系統的/lib/路徑下,並執行ldconfig命令(此操作需要root許可權才能搞定);
方法二:將libprint_func.so所在路徑,增加到LD_LIBRARY_PATH環境變數中。
1)先使用方法二解決:
$ export LD_LIBRARY_PATH=./lib
$ ldd ./test
linux-vdso.so.1 => (0x00007ffeb81dd000)
libprint_func.so => ./lib/libprint_func.so (0x00007f35a3dfc000)
libc.so.6 => /lib64/libc.so.6 (0x00007f35a3a25000)
/lib64/ld-linux-x86-64.so.2 (0x00005625bf8b5000)
$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
2)再使用方法一解決:
$ export LD_LIBRARY_PATH=./
$ ./test
./test: error while loading shared libraries: libprint_func.so: cannot open shared object file: No such file or directory
$ sudo cp lib/libprint_func.so /lib
$ sudo ldconfig
$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
5 擴充套件
問題一:如果在不同的SO中存在相同的函式名,那麼如何處理?
在之前test.c中的程式碼基礎上,修改print_func函式內容:
#include <stdio.h>
void print_func(void)
{
int i = 0;
for (i = 10; i < 20; i++)
printf("i = %d\n", i);
return;
}
然後重新生成一個SO檔案,重新命名為libprint_func00.so(更改Makefile)。
先刪除/lib庫下的libprint_func.so。
$ sudo rm /lib/libprint_func.so
$ sudo ldconfig
將libprint_func.so 和 libprint_func00.so 都放到當前目錄的lib下:
$ ls ./lib/
libprint_func00.so* libprint_func.so*
然後使用方法二連結SO檔案:
$ export LD_LIBRARY_PATH=./lib
編譯:
$ gcc -o test main.c -lprint_func -lprint_func00 -Llib -Iinclude -Wall
$ ldd test
linux-vdso.so.1 => (0x00007ffd04d9b000)
libprint_func.so => ./lib/libprint_func.so (0x00007f729a480000)
libprint_func00.so => ./lib/libprint_func00.so (0x00007f729a27d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f7299ea7000)
/lib64/ld-linux-x86-64.so.2 (0x000055f88ea81000)
執行:
$ ./test
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
可以看出,執行時呼叫的是libprint_func.so庫。
下面更改編譯時SO檔案的順序(先-lprint_func00),然後執行:
$ gcc -o test main.c -lprint_func00 -lprint_func -Llib -Iinclude -Wall
$ ./test
i = 10
i = 11
i = 12
i = 13
i = 14
i = 15
i = 16
i = 17
i = 18
i = 19
可以看出,執行時呼叫的是libprint_func00.so庫。
總結:SO中重名時,優先呼叫的是先連結的庫。
對於不同庫中函式重名的問題,可參考部落格Linux 動態庫同名函式處理-iibull-ChinaUnix部落格,這裡不進行說明。
6 總結
由於這是第一次嘗試生成SO檔案,部分地方可能存在問題,希望網友指出。
再次感謝參考的部落格:
C編譯: 動態連線庫 (.so檔案) - Vamei - 部落格園 (cnblogs.com)
C 多個動態庫存在同名函式問題處理方法:-fvisibility=hidden_more_HH-CSDN部落格_fvisibility
Linux編譯動態連結庫so避免執行時才發現函式未定義符號的錯誤undefined symbol的ld引數 (gmd20.github.io)
檢視so庫的方法__臣本布衣_新浪部落格 (sina.com.cn)
Linux 動態庫同名函式處理-iibull-ChinaUnix部落格
Linux下部分用於SO相關的命令:
列印SO中的符號資訊:nm -D libxxx.so
$ nm -D ./lib/libprint_func.so
0000000000201000 B __bss_start
w __cxa_finalize
0000000000201000 B _edata
0000000000201008 B _end
0000000000000700 T _fini
w __gmon_start__
0000000000000580 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U printf
00000000000006c5 T print_func
檢視依賴關係:ldd libxxx.so
$ ldd ./lib/libprint_func.so
linux-vdso.so.1 => (0x00007fff56150000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd3fd4e7000)
/lib64/ld-linux-x86-64.so.2 (0x00005598885c5000)
檢視so庫的屬性:file libxxx.so
$ file ./lib/libprint_func.so
./lib/libprint_func.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=24735da88c6198d81974645c25367fae14a267a5, stripped