Linux下編譯生成SO並進行呼叫執行

zhengcixi發表於2021-12-21

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 程式碼結構

image-20211221100302725

(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檔案。

image-20211221101011250

$ 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

相關文章