Home 文章轉錄--Linux環境撰寫Shared Library
Post
Cancel

文章轉錄--Linux環境撰寫Shared Library

撰寫範例程式

1
bool isPalindrome(char* word);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "pal.h"
#include <string.h>
 
bool isPalindrome(char* word)
{
    bool ret = true;
 
    char *p = word;
    int len = strlen(word);
    char *q = &word[len-1];
 
    for (int i = 0 ; i < len ; ++i, ++p, --q)
    {
        if (*p != *q)
        {
            ret = false;
        }
    }
 
    return ret;
}

編譯shared library

在終端機輸入以下GCC指令,注意這裡我們有-c選項,這告訴GCC不要進行linking stage,如果沒加GCC就會報錯,因為一個應用程式一定會有main()函式,但是Library不需要main()函式。這行指令將會產生一個pal.o

1
g++ -fPIC -c -Wall pal.cpp

接下來我們要用pal.o檔和以下指令產生真正的library。ld是linker program,通常會被g++呼叫。-shared告訴ld製作一個shared object,以pal.o為輸入輸出名為libpal.so。在Linux通常shared libraries的副檔名都是.so。

1
ld -shared pal.o -o libpal.so

使用shared library

首先建立一個main.cpp來呼叫我們的library的isPalindrome函式。在程式中我們include函式庫的標頭檔pal.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "pal.h"
#include <iostream>
 
using namespace std;
 
int main()
{
    while (1)
    {
        char buffer[64] = {0};
        cin >> buffer;
 
        if (isPalindrome(buffer))
        {
            cout << "Word is a palindrome" << endl;
        }
        else
        {
            cout << "Word is not a palindrome" << endl;
        }
    }
 
    return 0;
}

接下來我們可能直接用g++ -Wall main.cpp -lpal這行指令連接我們的shared library,c但是如果這麼做我們會得到下面錯誤訊息。這是因為ld在預設的搜尋路徑下找不到libpal.so

1
2
/usr/bin/ld: cannot find -lpal: No such file or directory
collect2: error: ld returned 1 exit status

其中一種解法是用g++告入ld函式庫的位置

1
g++ -Wall -L<libpal.so的路徑> -Wl,-rpath=<libpal.so的路徑> main.cpp -lpal

例如

1
g++ -Wall -L/home/faye -Wl,-rpath=/home/faye/ main.cpp -lpal

其中:

  • -Wall是用來檢查所有編譯警告的
  • L式shared library的路徑,讓ld知道要去那裡尋找
  • -Wl是一連串用逗號分隔的linker指令,在這裡有
    • -rpath表示library 的路徑會被嵌入到主程式的執行檔,因此loader在執行主程式的時候可以找到library

-L-rpath的區別是:

  • -L是給linker用的
  • -rpath是被嵌入到執行檔給loader看的

最後g++會幫我們產生a.out執行檔,執行方式和結果如下

1
2
3
4
5
$ ./a.out 
ada
Word is a palindrome
team
Word is not a palindrome

安裝自己開發的Shared Libraries

用剛剛的方式連接Shared Libraries的方法有個缺點,也就是你的編譯指令直接給其他人的話可能會出錯,因為Shared Libraries在每個人的電腦上的位置可能都不一樣。因此rpath-L選項的路徑也會不一樣。而我們的解決方法之一就是LD_LIBRARY_PATH

LD_LIBRARY_PATH

如果我們直接在終端機輸入指令echo $LD_LIBRARY_PATHLD_LIBRARY_PATH的值,他應該會是空的,除非你以前曾經設定過他。要是用LD_LIBRARY_PATH我們只需要以下指令

1
export LD_LIBRARY_PATH=<Shared Library所在資料夾>:$LD_LIBRARY_PATH

例如

1
export LD_LIBRARY_PATH=/home/faye:$LD_LIBRARY_PATH

這時在輸入一次echo $LD_LIBRARY_PATH應該就會輸出/home/faye:。而這時候我們編譯main.cpp的時候就算沒有-rpath選項編譯出來的執行檔也不會找不到libpal.so

1
g++ -Wall -L/home/faye/sotest main.cpp -lpal

你可以先嘗試看看在還沒設定LD_LIBRARY_PATH之前或是利用unset LD_LIBRARY_PATH指令清空LD_LIBRARY_PATH變數,所編譯出來的執行檔會出現什麼問題。
你應該會發現編譯的過程沒有任何錯誤訊息,但是一旦你執行編譯出來的執行檔a.out就會跳出錯誤訊息./a.out: error while loading shared libraries: libpal.so: cannot open shared object file: No such file or directory,這表示執行檔loader找不到libpal.so

不過LD_LIBRARY_PATH其實只適合在開發階段拿來測試library用,因為它不需要root權限,但是函式庫發布給大家使用的時候,要求每個人都去設定LD_LIBRARY_PATH並不是個好方法。

ldconfig : 安裝Shared Libraries正統方法

首先我們先清除上一個教學的LD_LIBRARY_PATH設定,可以用unset LD_LIBRARY_PATH指令來清除。
接下來我們必須把我們的函式庫複製到/usr/lib資料夾。複製函式庫到/usr/lib必須擁有root權限,因此我們用sudo來幫助我們。

1
sudo mv <函式庫所在資料夾>/libpal.so /usr/lib

例如

1
sudo mv /home/faye/libpal.so /usr/lib

接下來更新系統中儲存可用libraries的cache。

1
sudo ldconfig

你可以用下面指令來確定cache已經被更新了而且系統可以找到你的函式庫

1
ldconfig -p | grep libpal

系統應該會回應你

1
libpal.so (libc6,x86-64) => /lib/libpal.so

接下來你就可以用下面指令編譯我們的執行檔了,而且這次我們不需要-rpath,也不需要-L選項!!因為現在我們的函式庫已經位於系統預設的函式庫搜尋路徑下了。

1
g++ -Wall main.cpp -lpal

參考:
https://www.fayewilliams.com/2015/07/07/creating-a-shared-library/
https://www.fayewilliams.com/2015/07/14/installing-and-accessing-shared-libraries/

相關文章:
Linux安裝prebuild函式庫以OpenCV為例

This post is licensed under CC BY 4.0 by the author.

Linux下設定VScode、CMake、GCC、GDB來開發C++專案

Linux安裝prebuild函式庫以OpenCV為例