解决Linux程序编译链接动态库版本的相关问题
<p><span><strong>前言</strong></span></p>
<p>
不同版本的动态库可能会不兼容,如果程序在编译时指定动态库是某个低版本,运行是用的一个高版本,可能会导致无法运行。Linux上对动态库的命名采用libxxx.so.a.b.c的格式,其中a代表大版本号,b代表小版本号,c代表更小的版本号,我们以Linux自带的cp程序为例,通过ldd查看其依赖的动态库</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_307630">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ ldd </code><code class="bash plain">/bin/cp</code> </div>
<div class="line number2 index1 alt1">
<code class="bash plain">linux-vdso.so.1 => (0x00007ffff59df000)</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">libselinux.so.1 => </code><code class="bash plain">/lib64/libselinux</code><code class="bash plain">.so.1 (0x00007fb3357e0000)</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">librt.so.1 => </code><code class="bash plain">/lib64/librt</code><code class="bash plain">.so.1 (0x00007fb3355d7000)</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">libacl.so.1 => </code><code class="bash plain">/lib64/libacl</code><code class="bash plain">.so.1 (0x00007fb3353cf000)</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">libattr.so.1 => </code><code class="bash plain">/lib64/libattr</code><code class="bash plain">.so.1 (0x00007fb3351ca000)</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">libc.so.6 => </code><code class="bash plain">/lib64/libc</code><code class="bash plain">.so.6 (0x00007fb334e35000)</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash plain">libdl.so.2 => </code><code class="bash plain">/lib64/libdl</code><code class="bash plain">.so.2 (0x00007fb334c31000)</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">/lib64/ld-linux-x86-64</code><code class="bash plain">.so.2 (0x00007fb335a0d000)</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash plain">libpthread.so.0 => </code><code class="bash plain">/lib64/libpthread</code><code class="bash plain">.so.0 (0x00007fb334a14000)</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
左边是依赖的动态库名字,右边是链接指向的文件,再查看libacl.so相关的动态库</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_872126">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ ll </code><code class="bash plain">/lib64/libacl</code><code class="bash plain">.so* </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">lrwxrwxrwx. 1 root root 15 1月 7 2015 </code><code class="bash plain">/lib64/libacl</code><code class="bash plain">.so.1 -> libacl.so.1.1.0</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">-rwxr-xr-x. 1 root root 31280 12月 8 2011 </code><code class="bash plain">/lib64/libacl</code><code class="bash plain">.so.1.1.0</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
我们发现libacl.so.1实际上是一个软链接,它指向的文件是libacl.so.1.1.0,命名方式符合我们上面的描述。也有不按这种方式命名的,比如</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_761361">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ ll </code><code class="bash plain">/lib64/libc</code><code class="bash plain">.so* </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">lrwxrwxrwx 1 root root 12 8月 12 14:18 </code><code class="bash plain">/lib64/libc</code><code class="bash plain">.so.6 -> libc-2.12.so</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
不管怎样命名,只要按照规定的方式来生成和使用动态库,就不会有问题。而且我们往往是在机器A上编译程序,在机器B上运行程序,编译和运行的环境其实是有略微不同的。下面就说说动态库在生成和使用过程中的一些问题</p>
<p>
<span><strong>动态库的编译</strong></span></p>
<p>
我们以一个简单的程序作为例子</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_140029">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">//</code> <code class="bash plain">filename:hello.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash comments">#include <stdio.h></code>
</div>
<div class="line number3 index2 alt2">
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">void hello(const char* name)</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"hello %s!\n"</code><code class="bash plain">, name);</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">}</code>
</div>
<div class="line number8 index7 alt1">
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">//</code> <code class="bash plain">filename:hello.h</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash plain">void hello(const char* name);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
采用如下命令进行编译</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_680682">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
需要注意的参数是<code>-Wl,soname</code>(中间没有空格),-Wl选项告诉编译器将后面的参数传递给链接器,<br><code>-soname</code>则指定了动态库的soname(简单共享名,Short for shared object name)</p>
<p>
现在我们生成了libhello.so.0.0.1,当我们运行<code>ldconfig -n .</code>命令时,当前目录会多一个软连接</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_211299">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ ll libhello.so.0 </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">lrwxrwxrwx 1 handy handy 17 8月 17 14:18 libhello.so.0 -> libhello.so.0.0.1</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这个软链接是如何生成的呢,并不是截取libhello.so.0.0.1名字的前面部分,而是根据libhello.so.0.0.1编译时指定的-soname生成的。也就是说我们在编译动态库时通过-soname指定的名字,已经记载到了动态库的二进制数据里面。不管程序是否按libxxx.so.a.b.c格式命名,但Linux上几乎所有动态库在编译时都指定了-soname,我们可以通过readelf工具查看soname,比如文章开头列举的两个动态库</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_751575">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ readelf -d </code><code class="bash plain">/lib64/libacl</code><code class="bash plain">.so.1.1.0 </code>
</div>
<div class="line number2 index1 alt1">
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">Dynamic section at offset 0x6de8 contains 24 entries:</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">Tag Type Name</code><code class="bash plain">/Value</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">0x0000000000000001 (NEEDED) Shared library: </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">0x0000000000000001 (NEEDED) Shared library: </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">0x000000000000000e (SONAME) Library soname: </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这里省略了一部分,可以看到最后一行SONAME为libacl.so.1,所以/lib64才会有一个这样的软连接</p>
<p>
再看libc-2.12.so文件,该文件并没有采用我们说的命名方式</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_185782">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ readelf -d </code><code class="bash plain">/lib64/libc-2</code><code class="bash plain">.12.so </code>
</div>
<div class="line number2 index1 alt1">
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">Dynamic section at offset 0x18db40 contains 27 entries:</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">Tag Type Name</code><code class="bash plain">/Value</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">0x0000000000000001 (NEEDED) Shared library: </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">0x000000000000000e (SONAME) Library soname: </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
同样可以看到最后一行SONAME为libc.so.6,即便该动态库没有按版本号的方式命名,但仍旧有一个软链指向该动态库,而该软链的名字就是soname指定的名字</p>
<p>
<strong>所以关键就是这个soname,它相当于一个中间者,当我们的动态库只是升级一个小版本时,我们可以让它的soname相同,而可执行程序只认soname指定的动态库,这样依赖这个动态库的可执行程序不需重新编译就能使用新版动态库的特性</strong></p>
<p>
<span><strong>可执行程序的编译</strong></span></p>
<p>
还是以hello动态库为例,我们写一个简单的程序</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_909029">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">//</code> <code class="bash plain">filename:main.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash comments">#include "hello.h"</code>
</div>
<div class="line number3 index2 alt2">
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">int main()</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">hello(</code><code class="bash string">"handy"</code><code class="bash plain">);</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash keyword">return</code> <code class="bash plain">0;</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
现在目录下是如下结构</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_918346">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── hello.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── hello.h</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.1</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">└── main.c</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
libhello.so.0.0.1是我们编译生成的动态库,libhello.so.0是通过ldconfig生成的链接,采用如下命令编译main.c</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_982435">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ gcc main.c -L. -lhello -o main </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">/usr/bin/ld</code><code class="bash plain">: cannot </code><code class="bash functions">find</code> <code class="bash plain">-lhello</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
报错找不到hello动态库,在Linux下,编译时指定-lhello,链接器会去寻找libhello.so这样的文件,当前目录下没有这个文件,所以报错。建立这样一个软链,目录结构如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_148541">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── hello.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── hello.h</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so -> libhello.so.0.0.1</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.1</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">└── main.c</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
让libhello.so链接指向实际的动态库文件libhello.so.0.0.1,再编译main程序</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_441345">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">gcc main.c -L. -lhello -o main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这样可执行文件就生成了。通过以上测试我们发现,在编译可执行程序时,链接器会去找它依赖的libxxx.so这样的文件,因此必须保证libxxx.so的存在</p>
<p>
用ldd查看其依赖的动态库</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_670762">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ ldd main </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">linux-vdso.so.1 => (0x00007fffe23f2000)</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">libhello.so.0 => not found</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">libc.so.6 => </code><code class="bash plain">/lib64/libc</code><code class="bash plain">.so.6 (0x00007fb6cd084000)</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">/lib64/ld-linux-x86-64</code><code class="bash plain">.so.2 (0x00007fb6cd427000)</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
我们发现main程序依赖的动态库名字是libhello.so.0,既不是libhello.so也不是libhello.so.0.0.1。其实在生成main程序的过程有如下几步</p>
<ol>
<li>
链接器通过编译命令-L. -lhello在当前目录查找libhello.so文件</li>
<li>
读取libhello.so链接指向的实际文件,这里是libhello.so.0.0.1</li>
<li>
读取libhello.so.0.0.1中的SONAME,这里是libhello.so.0</li>
<li>
将libhello.so.0记录到main程序的二进制数据里</li>
</ol>
<p>
也就是说libhello.so.0是已经存储到main程序的二进制数据里的,不管这个程序在哪里,通过ldd查看它依赖的动态库都是libhello.so.0</p>
<p>
而为什么这里ldd查看main显示libhello.so.0为not found呢,因为ldd是从环境变量$LD_LIBRARY_PATH指定的路径里来查找文件的,我们指定环境变量再运行如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_771148">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ </code><code class="bash functions">export</code> <code class="bash plain">LD_LIBRARY_PATH=. && ldd main </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">linux-vdso.so.1 => (0x00007fff7bb63000)</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">libhello.so.0 => .</code><code class="bash plain">/libhello</code><code class="bash plain">.so.0 (0x00007f2a3fd39000)</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">libc.so.6 => </code><code class="bash plain">/lib64/libc</code><code class="bash plain">.so.6 (0x00007f2a3f997000)</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">/lib64/ld-linux-x86-64</code><code class="bash plain">.so.2 (0x00007f2a3ff3b000)</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
<span><strong>可执行程序的运行</strong></span></p>
<p>
现在测试目录结果如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_641797">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── hello.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── hello.h</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so -> libhello.so.0.0.1</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.1</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">├── main</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">└── main.c</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这里我们把编译环境和运行环境混在一起了,不过没关系,只要我们知道其中原理,就可以将其理清楚</p>
<p>
前面我们已经通过ldd查看了main程序依赖的动态库,并且指定了LD_LIBRARY_PATH变量,现在就可以直接运行了</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_799096">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ .</code><code class="bash plain">/main</code> </div>
<div class="line number2 index1 alt1">
<code class="bash plain">hello Handy!</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
看起来很顺利。那么如果我们要部署运行环境,该怎么部署呢。显然,源代码是不需要的,我们只需要动态库和可执行程序。这里新建一个运行目录,并拷贝相关文件,目录结构如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_250381">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">└── main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这时运行会main会发现</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_154382">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ .</code><code class="bash plain">/main</code> </div>
<div class="line number2 index1 alt1">
<code class="bash plain">.</code><code class="bash plain">/main</code><code class="bash plain">: error </code><code class="bash keyword">while</code> <code class="bash plain">loading shared libraries: libhello.so.0: cannot </code><code class="bash functions">open</code> <code class="bash plain">shared object </code><code class="bash functions">file</code><code class="bash plain">: No such </code><code class="bash functions">file</code> <code class="bash plain">or directory</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
报错说libhello.so.0文件找不到,也就是说程序运行时需要寻找的动态库文件名其实是动态库编译时指定的SONAME,这也和我们用ldd查看的一致。通过<code>ldconfig -n .</code>建立链接,如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_582822">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.1</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">└── main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
再运行程序,结果就会符合预期了</p>
<p>
从上面的测试看出,程序在运行时并不需要知道libxxx.so,而是需要程序本身记载的该动态库的SONAME,所以main程序的运行环境只需要以上三个文件即可</p>
<p>
<span><strong>动态库版本更新</strong></span></p>
<p>
假设动态库需要做一个小小的改动,如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_176802">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">//</code> <code class="bash plain">filename:hello.c</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash comments">#include <stdio.h></code>
</div>
<div class="line number3 index2 alt2">
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">void hello(const char* name)</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"hello %s, welcom to our world!\n"</code><code class="bash plain">, name);</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
由于改动较小,我们编译动态库时仍然指定相同的soname</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_233181">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
将新的动态库拷贝到运行目录,此时运行目录结构如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_168961">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.1</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so.0.0.2</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">└── main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
此时目录下有两个版本的动态库,但libhello.so.0指向的是老本版,运行<code>ldconfig -n .</code>后我们发现,链接指向了新版本,如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_663089">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.2</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so.0.0.2</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">└── main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
再运行程序</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_341554">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$ .</code><code class="bash plain">/main</code> </div>
<div class="line number2 index1 alt1">
<code class="bash plain">hello Handy, welcom to our world!</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
没有重新编译就使用上了新的动态库, wonderful!</p>
<p>
同样,假如我们的动态库有大的改动,编译动态库时指定了新的soname,如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_355388">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0.0</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
将动态库文件拷贝到运行目录,并执行<code>ldconfig -n . </code>,目录结构如下</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_951251">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">├── libhello.so.0 -> libhello.so.0.0.2</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">├── libhello.so.0.0.1</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">├── libhello.so.0.0.2</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">├── libhello.so.1 -> libhello.so.1.0.0</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">├── libhello.so.1.0.0</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">└── main</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
这时候发现,生成了新的链接libhello.so.1,而main程序还是使用的libhello.so.0,所以无法使用新版动态库的功能,需要重新编译才行</p>
<p>
总结</p>
<p>
在实际生产环境中,程序的编译和运行往往是分开的,但只要搞清楚这一系列过程中的原理,就不怕被动态库的版本搞晕。简单来说,按如下方式来做</p>
<ol>
<li>
编译动态库时指定<code>-Wl</code>, <code>-soname</code> ,<code>libxxx.so.a</code>,设置soname为libxxx.so.a,生成实际的动态库文件libxxx.so.a.b.c,</li>
<li>
编译可执行程序时保证libxx.so存在,如果是软链,必须指向实际的动态库文件libxxx.so.a.b.c</li>
<li>
运行可执行文件时保证libxxx.so.a.b.c文件存在,通过ldconfig生成libxxx.so.a链接指向libxxx.so.a.b.c</li>
<li>
设置环境变量LD_LIBRARY_PATH,运行可执行程序</li>
</ol>
<p>
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。</p>
頁:
[1]