亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 服務器 > Linux服務器 > 正文

解決Linux程序編譯鏈接動態庫版本的相關問題

2024-09-05 23:03:33
字體:
來源:轉載
供稿:網友

前言

不同版本的動態庫可能會不兼容,如果程序在編譯時指定動態庫是某個低版本,運行是用的一個高版本,可能會導致無法運行。Linux上對動態庫的命名采用libxxx.so.a.b.c的格式,其中a代表大版本號,b代表小版本號,c代表更小的版本號,我們以Linux自帶的cp程序為例,通過ldd查看其依賴的動態庫

 $ ldd /bin/cp            linux-vdso.so.1 => (0x00007ffff59df000)libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fb3357e0000)librt.so.1 => /lib64/librt.so.1 (0x00007fb3355d7000)libacl.so.1 => /lib64/libacl.so.1 (0x00007fb3353cf000)libattr.so.1 => /lib64/libattr.so.1 (0x00007fb3351ca000)libc.so.6 => /lib64/libc.so.6 (0x00007fb334e35000)libdl.so.2 => /lib64/libdl.so.2 (0x00007fb334c31000)/lib64/ld-linux-x86-64.so.2 (0x00007fb335a0d000)libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb334a14000)

左邊是依賴的動態庫名字,右邊是鏈接指向的文件,再查看libacl.so相關的動態庫

 $ ll /lib64/libacl.so*           lrwxrwxrwx. 1 root root 15 1月 7 2015 /lib64/libacl.so.1 -> libacl.so.1.1.0-rwxr-xr-x. 1 root root 31280 12月 8 2011 /lib64/libacl.so.1.1.0

我們發現libacl.so.1實際上是一個軟鏈接,它指向的文件是libacl.so.1.1.0,命名方式符合我們上面的描述。也有不按這種方式命名的,比如

$ ll /lib64/libc.so*            lrwxrwxrwx 1 root root 12 8月 12 14:18 /lib64/libc.so.6 -> libc-2.12.so

不管怎樣命名,只要按照規定的方式來生成和使用動態庫,就不會有問題。而且我們往往是在機器A上編譯程序,在機器B上運行程序,編譯和運行的環境其實是有略微不同的。下面就說說動態庫在生成和使用過程中的一些問題

動態庫的編譯

我們以一個簡單的程序作為例子

// filename:hello.c#include <stdio.h>void hello(const char* name){ printf("hello %s!/n", name);}// filename:hello.hvoid hello(const char* name);

采用如下命令進行編譯

gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1

需要注意的參數是-Wl,soname(中間沒有空格),-Wl選項告訴編譯器將后面的參數傳遞給鏈接器,
-soname則指定了動態庫的soname(簡單共享名,Short for shared object name)

現在我們生成了libhello.so.0.0.1,當我們運行ldconfig -n .命令時,當前目錄會多一個軟連接

 $ ll libhello.so.0            lrwxrwxrwx 1 handy handy 17 8月 17 14:18 libhello.so.0 -> libhello.so.0.0.1

這個軟鏈接是如何生成的呢,并不是截取libhello.so.0.0.1名字的前面部分,而是根據libhello.so.0.0.1編譯時指定的-soname生成的。也就是說我們在編譯動態庫時通過-soname指定的名字,已經記載到了動態庫的二進制數據里面。不管程序是否按libxxx.so.a.b.c格式命名,但Linux上幾乎所有動態庫在編譯時都指定了-soname,我們可以通過readelf工具查看soname,比如文章開頭列舉的兩個動態庫

 $ readelf -d /lib64/libacl.so.1.1.0                     Dynamic section at offset 0x6de8 contains 24 entries:Tag Type    Name/Value0x0000000000000001 (NEEDED)  Shared library: [libattr.so.1]0x0000000000000001 (NEEDED)  Shared library: [libc.so.6]0x000000000000000e (SONAME)  Library soname: [libacl.so.1]

這里省略了一部分,可以看到最后一行SONAME為libacl.so.1,所以/lib64才會有一個這樣的軟連接

再看libc-2.12.so文件,該文件并沒有采用我們說的命名方式

 $ readelf -d /lib64/libc-2.12.so                     Dynamic section at offset 0x18db40 contains 27 entries:Tag Type    Name/Value0x0000000000000001 (NEEDED)  Shared library: [ld-linux-x86-64.so.2]0x000000000000000e (SONAME)  Library soname: [libc.so.6]

同樣可以看到最后一行SONAME為libc.so.6,即便該動態庫沒有按版本號的方式命名,但仍舊有一個軟鏈指向該動態庫,而該軟鏈的名字就是soname指定的名字

所以關鍵就是這個soname,它相當于一個中間者,當我們的動態庫只是升級一個小版本時,我們可以讓它的soname相同,而可執行程序只認soname指定的動態庫,這樣依賴這個動態庫的可執行程序不需重新編譯就能使用新版動態庫的特性

可執行程序的編譯

還是以hello動態庫為例,我們寫一個簡單的程序

// filename:main.c#include "hello.h"int main(){ hello("handy"); return 0;}

現在目錄下是如下結構

├── hello.c├── hello.h├── libhello.so.0 -> libhello.so.0.0.1├── libhello.so.0.0.1└── main.c

libhello.so.0.0.1是我們編譯生成的動態庫,libhello.so.0是通過ldconfig生成的鏈接,采用如下命令編譯main.c

 $ gcc main.c -L. -lhello -o main                      /usr/bin/ld: cannot find -lhello

報錯找不到hello動態庫,在Linux下,編譯時指定-lhello,鏈接器會去尋找libhello.so這樣的文件,當前目錄下沒有這個文件,所以報錯。建立這樣一個軟鏈,目錄結構如下

├── hello.c├── hello.h├── libhello.so -> libhello.so.0.0.1├── libhello.so.0 -> libhello.so.0.0.1├── libhello.so.0.0.1└── main.c

讓libhello.so鏈接指向實際的動態庫文件libhello.so.0.0.1,再編譯main程序

gcc main.c -L. -lhello -o main

這樣可執行文件就生成了。通過以上測試我們發現,在編譯可執行程序時,鏈接器會去找它依賴的libxxx.so這樣的文件,因此必須保證libxxx.so的存在

用ldd查看其依賴的動態庫

 $ ldd main                         linux-vdso.so.1 => (0x00007fffe23f2000) libhello.so.0 => not found libc.so.6 => /lib64/libc.so.6 (0x00007fb6cd084000) /lib64/ld-linux-x86-64.so.2 (0x00007fb6cd427000)

我們發現main程序依賴的動態庫名字是libhello.so.0,既不是libhello.so也不是libhello.so.0.0.1。其實在生成main程序的過程有如下幾步

  1. 鏈接器通過編譯命令-L. -lhello在當前目錄查找libhello.so文件
  2. 讀取libhello.so鏈接指向的實際文件,這里是libhello.so.0.0.1
  3. 讀取libhello.so.0.0.1中的SONAME,這里是libhello.so.0
  4. 將libhello.so.0記錄到main程序的二進制數據里

也就是說libhello.so.0是已經存儲到main程序的二進制數據里的,不管這個程序在哪里,通過ldd查看它依賴的動態庫都是libhello.so.0

而為什么這里ldd查看main顯示libhello.so.0為not found呢,因為ldd是從環境變量$LD_LIBRARY_PATH指定的路徑里來查找文件的,我們指定環境變量再運行如下

 $ export LD_LIBRARY_PATH=. && ldd main                     linux-vdso.so.1 => (0x00007fff7bb63000) libhello.so.0 => ./libhello.so.0 (0x00007f2a3fd39000) libc.so.6 => /lib64/libc.so.6 (0x00007f2a3f997000) /lib64/ld-linux-x86-64.so.2 (0x00007f2a3ff3b000)

可執行程序的運行

現在測試目錄結果如下

├── hello.c├── hello.h├── libhello.so -> libhello.so.0.0.1├── libhello.so.0 -> libhello.so.0.0.1├── libhello.so.0.0.1├── main└── main.c

這里我們把編譯環境和運行環境混在一起了,不過沒關系,只要我們知道其中原理,就可以將其理清楚

前面我們已經通過ldd查看了main程序依賴的動態庫,并且指定了LD_LIBRARY_PATH變量,現在就可以直接運行了

 $ ./main                        hello Handy!

看起來很順利。那么如果我們要部署運行環境,該怎么部署呢。顯然,源代碼是不需要的,我們只需要動態庫和可執行程序。這里新建一個運行目錄,并拷貝相關文件,目錄結構如下

├── libhello.so.0.0.1└── main

這時運行會main會發現

 $ ./main                        ./main: error while loading shared libraries: libhello.so.0: cannot open shared object file: No such file or directory

報錯說libhello.so.0文件找不到,也就是說程序運行時需要尋找的動態庫文件名其實是動態庫編譯時指定的SONAME,這也和我們用ldd查看的一致。通過ldconfig -n .建立鏈接,如下

├── libhello.so.0 -> libhello.so.0.0.1├── libhello.so.0.0.1└── main

再運行程序,結果就會符合預期了

從上面的測試看出,程序在運行時并不需要知道libxxx.so,而是需要程序本身記載的該動態庫的SONAME,所以main程序的運行環境只需要以上三個文件即可

動態庫版本更新

假設動態庫需要做一個小小的改動,如下

// filename:hello.c#include <stdio.h>void hello(const char* name){ printf("hello %s, welcom to our world!/n", name);}

由于改動較小,我們編譯動態庫時仍然指定相同的soname

gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2

將新的動態庫拷貝到運行目錄,此時運行目錄結構如下

├── libhello.so.0 -> libhello.so.0.0.1├── libhello.so.0.0.1├── libhello.so.0.0.2└── main

此時目錄下有兩個版本的動態庫,但libhello.so.0指向的是老本版,運行ldconfig -n .后我們發現,鏈接指向了新版本,如下

├── libhello.so.0 -> libhello.so.0.0.2├── libhello.so.0.0.1├── libhello.so.0.0.2└── main

再運行程序

 $ ./main                        hello Handy, welcom to our world!

沒有重新編譯就使用上了新的動態庫, wonderful!

同樣,假如我們的動態庫有大的改動,編譯動態庫時指定了新的soname,如下

gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0.0

將動態庫文件拷貝到運行目錄,并執行ldconfig -n . ,目錄結構如下

├── libhello.so.0 -> libhello.so.0.0.2├── libhello.so.0.0.1├── libhello.so.0.0.2├── libhello.so.1 -> libhello.so.1.0.0├── libhello.so.1.0.0└── main

這時候發現,生成了新的鏈接libhello.so.1,而main程序還是使用的libhello.so.0,所以無法使用新版動態庫的功能,需要重新編譯才行

總結

在實際生產環境中,程序的編譯和運行往往是分開的,但只要搞清楚這一系列過程中的原理,就不怕被動態庫的版本搞暈。簡單來說,按如下方式來做

  1. 編譯動態庫時指定-Wl, -soname ,libxxx.so.a,設置soname為libxxx.so.a,生成實際的動態庫文件libxxx.so.a.b.c,
  2. 編譯可執行程序時保證libxx.so存在,如果是軟鏈,必須指向實際的動態庫文件libxxx.so.a.b.c
  3. 運行可執行文件時保證libxxx.so.a.b.c文件存在,通過ldconfig生成libxxx.so.a鏈接指向libxxx.so.a.b.c
  4. 設置環境變量LD_LIBRARY_PATH,運行可執行程序

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情一区二区三区在线视频观看| 久久免费精品视频| 国产精品网站入口| 色一区av在线| 美女福利精品视频| 国产成人免费av电影| 久久久欧美一区二区| 日韩精品电影网| 在线精品视频视频中文字幕| 久久影院免费观看| 久久久亚洲福利精品午夜| 国产91|九色| 亚洲精品国精品久久99热| 久久亚洲国产精品成人av秋霞| 亚洲国产一区二区三区四区| 97av在线视频| 91av在线网站| 色偷偷偷综合中文字幕;dd| 性欧美办公室18xxxxhd| 欧美三级欧美成人高清www| 欧美精品videofree1080p| 国产精品麻豆va在线播放| 国语自产精品视频在免费| 欧美国产日韩一区| 亚洲欧美制服中文字幕| 色偷偷av一区二区三区乱| 成人97在线观看视频| 亚洲男女自偷自拍图片另类| 国产精品久久久久久网站| 欧美激情乱人伦一区| 国产精品国模在线| 国产午夜精品美女视频明星a级| 亚洲高清免费观看高清完整版| 亚洲网站在线看| 欧美色videos| 91高潮在线观看| 亚洲欧美国产精品专区久久| 成人精品福利视频| 日韩成人在线观看| 国产欧美精品在线播放| 成人h片在线播放免费网站| 亚洲美女精品成人在线视频| 国产精品免费观看在线| 少妇高潮久久久久久潘金莲| 亚洲精品ady| 亚洲视频一区二区| 久久久999成人| 欧美日韩国产中字| 国产精品视频白浆免费视频| 国产精自产拍久久久久久蜜| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产精品成熟老女人| 国产精品xxx视频| 91久久精品国产91性色| 国产男人精品视频| 亚洲精品美女在线| 亚洲色图17p| 97在线免费观看| 欧美日韩加勒比精品一区| 欧美日韩国产麻豆| 91久久精品美女高潮| 亚洲一区二区三区四区在线播放| 久久这里只有精品视频首页| 91在线观看免费观看| 欧美大片在线影院| 日韩在线视频中文字幕| 九九九久久久久久| 日韩av在线直播| 日韩精品中文字幕久久臀| 热久久免费视频精品| 日韩在线观看成人| 成人午夜在线视频一区| 青青久久aⅴ北条麻妃| 日韩美女福利视频| 国产欧美亚洲视频| 亚洲欧美激情精品一区二区| 都市激情亚洲色图| 日韩欧美在线播放| 日韩av免费看网站| 国产一区二区三区在线视频| 国产精品永久在线| 国产精品爱久久久久久久| 4438全国成人免费| 久久久久久成人精品| 日韩视频免费在线| 亚洲人成电影网站色xx| 国产精品福利网站| 亚洲国产成人久久综合一区| 国产一区二区三区网站| 国产精品第一视频| 成人精品网站在线观看| 少妇av一区二区三区| 97精品国产97久久久久久免费| 欧美午夜丰满在线18影院| 欧美性猛交xxxx免费看久久久| 岛国av一区二区在线在线观看| 国产伦精品免费视频| 中文字幕亚洲无线码a| 亚洲精品资源在线| 欧美日韩一区二区精品| 欧美成人一区二区三区电影| 欧美一级电影免费在线观看| 精品一区二区三区三区| 久久av中文字幕| 最近免费中文字幕视频2019| 狠狠躁18三区二区一区| 久久精品国产电影| 欧美成人中文字幕在线| 欧美壮男野外gaytube| 97在线免费视频| 亚洲国产精品一区二区久| 久久天天躁日日躁| 亚洲精品小视频在线观看| 中文字幕一区日韩电影| 8050国产精品久久久久久| 久久久久久午夜| 欧美亚洲成人精品| 中文字幕亚洲综合久久| 国产精品日韩欧美| 成人福利视频在线观看| 欧美在线性爱视频| 亚洲毛片在线免费观看| 亚洲成人精品久久久| 亚洲人成伊人成综合网久久久| 97免费中文视频在线观看| 性色av香蕉一区二区| 亚洲精品一区av在线播放| 国产精品久久久久久久久久新婚| 夜夜嗨av色一区二区不卡| 日本精品视频在线观看| 97在线日本国产| 热久久免费国产视频| 91精品国产自产在线观看永久| 国产美女久久精品| 色天天综合狠狠色| 国产91精品久久久| 欧美精品第一页在线播放| 九九热精品视频| 欧美日韩久久久久| 亚洲高清久久久久久| 国产日韩欧美中文在线播放| 日韩三级成人av网| 色婷婷亚洲mv天堂mv在影片| 成人免费在线视频网站| 亚洲人成电影网站| 欧美中在线观看| 亚洲美女激情视频| 国产日韩欧美综合| 久久亚洲一区二区三区四区五区高| 亚洲国产精品字幕| 欧美视频免费在线| 国产丝袜一区二区| 在线播放日韩av| 日韩黄色在线免费观看| 精品久久久久久中文字幕大豆网| 日韩欧美成人精品| 国产精品久久久久999| 久久频这里精品99香蕉| 奇米成人av国产一区二区三区| 色视频www在线播放国产成人| 日韩成人中文字幕在线观看| 日韩av免费网站| 亚洲综合精品一区二区| 欧美限制级电影在线观看|