<p><strong>1.库文件简介</strong>
</p>
<p>库文件是一个包含了编译后代码、数据的文件,用于与程序其他代码连编,它可以使得程序模块化、编译速度更快,并且易于更新。库文件分为三种(<strong>实质为两种,在随后两句话有解释</strong>
):静态库(在程序之前就已经装载进其中了)、共享库(在程序启动之时加载进去,在程序直接共享)、动态加载库(dynamically loaded,DL)(在程序运行中任何时候都可以被加载进程序中使用,<strong>事实上DL并非是一个完全不同的库类型,共享库可以用作DL而被动态加载(静态库在Linux貌似无法用dlopen加载)</strong>
。注意有些人使用dynamically <em>linked</em>
libraries (DLLs)来指代共享库,有些人使用DLL这个词来形容任何可以被用作DL的库文件,这个请区分对待。</p>
<p>在具体使用中,我们应该多使用共享库,这使得用户可以独立于使用该库文件的程序而更新库。DL的确非常有用,但有时候我们可能并不需要那些灵活性,而对于静态库,由于更新起来实在费劲,我们一般不使用。</p>
<p><strong>2.静态库的建立</strong>
</p>
<p>静
态库就是一堆普通的目标文件(object
file),习惯上静态库以.a为后缀,这是使用ar命令生成的。静态库允许用户不用重新编译代码就可以链接程序,以节省重新编译的时间,其实这个时间已
经在强大的机器配置和快速的编译器中显得微不足道了,这个常常用来提供程序而不是源代码。速度上,静态ELF(Executable and
Linking Format)库文件比共享库或者动态加载库快1%-5%,但实际上常常因为其他因素而并不一定快。</p>
<p>我们写主文件prog.c:</p>
<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #include
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: <span style="color: #0000ff;">void</span>
ctest1(<span style="color: #0000ff;">int</span>
*);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: <span style="color: #0000ff;">void</span>
ctest2(<span style="color: #0000ff;">int</span>
*);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">int</span>
main()
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: <span style="color: #0000ff;">int</span>
x;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: ctest1(&x);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: printf("<span style="color: #8b0000;">Valx=%d/n</span>
",x);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11: <span style="color: #0000ff;">return</span>
0;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: }
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: </pre>
</pre>
<p>然后写这两个函数的实现:</p>
<p>ctest1.c</p>
<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: <span style="color: #0000ff;">void</span>
ctest1(<span style="color: #0000ff;">int</span>
*i)
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: *i=5;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: } </pre>
</pre>
<p>ctest2.c</p>
<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: <span style="color: #0000ff;">void</span>
ctest2(<span style="color: #0000ff;">int</span>
*i)
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: *i=100;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: }</pre>
</pre>
<p>我们首先编译这两个函数实现的源文件:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -Wall -c ctest1.c ctest2.c
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls
<br>
ctest1.c ctest1.o ctest2.c ctest2.o prog.c</p>
</blockquote>
<p>然后创建静态库libctest.a:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -cvq libctest.a ctest1.o ctest2.o</p>
<p>a - ctest1.o
<br>
a - ctest2.o</p>
</blockquote>
<p>我们查看一下这个库中的文件:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ar -t libctest.a
<br>
ctest1.o
<br>
ctest2.o</p>
</blockquote>
<p>此时我们可以编译我们的程序了,注意-l选项,后边的参数是去掉lib和.a的部分,并且需要放在要编译的文件名之后,否则会报错。:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ gcc -o test prog.c -L./ –lctest
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ls
<br>
ctest1.c ctest1.o ctest2.c ctest2.o libctest.a prog.c test
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/statics$ ./test
<br>
Valx=5</p>
</blockquote>
<p><strong>3.共享库的建立</strong>
</p>
<p>共享库是在程序启动时加载的库文件。当共享库加载完毕后所有启动的起来的程序都将使用新的共享库。在创建共享库之前,还需要了解一些知识:</p>
<ul>
<li>命名规则:
<ul>
<li>每一个共享库都有一个soname,一般都形如libname.so.versionNumber,其中versionNumber
每当接口发生改变时都要增加,一个完全的soname的前缀应该是它所在目录,在一个实际系统中,一个完整的soname只是共享库文件的real
name的符号链接。<strong>程序运行时在内部列出所需的共享库时使用的就是soname。</strong>
</li>
<li>每一个共享库也有一个real name,这是包含实际代码的文件名,real name使用soname为前缀,并且在后边添加一些信息,一般都形如soname.MinorNumber.ReleaseNumber。 最后的releaseNumber可有可无。<strong>这个是生成共享库时实际文件的名称。</strong>
</li>
<li>同时,在编译器要求使用一个共享库时使用的名字称为linker name,一般都是去掉版本号的soname,<strong>用于gcc中-lname这样的选项的编译。</strong>
</li>
<li>这几个名字的关系:你在创建实际库文件中指定libreadline.so.3.0为real name
,并且使用符号链接创建soname ->libreadline.so.3和linker name->
/usr/lib/libreadline.so。 </li>
</ul>
</li>
<li>放置位置:
<ul>
<li>GNU标准推荐将所有默认的库安装在/usr/local/lib,这指的是开发者源代码默认的位置。 </li>
<li>FHS指出大多数的库文件应该放在/usr/lib,而启动所需的库则应该放在/lib中,而非系统库应该放在/usr/local/lib。这指的是发行版默认的位置,这两个标准并没有矛盾。 </li>
</ul>
</li>
</ul>
<p>共享库的主要有三个步骤:</p>
<ul>
<li>创建目标代码。 </li>
<li>创建库。 </li>
<li>使用符号链接创建默认版本的共享库(可选)。 </li>
</ul>
<p>现在我们举个例子来说明,首先我们编译源代码,使用-fPIC选项生成共享库所需的位置独立代码(position-independent code (PIC)):</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -fPIC -c *.c
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
<br>
ctest1.c ctest1.o ctest2.c ctest2.o prog.c prog.o</p>
</blockquote>
<p>然后我们创建库文件:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0 *.o
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
<br>
ctest1.c ctest1.o ctest2.c ctest2.o <strong>libctest.so.1.0</strong>
prog.c prog.o</p>
</blockquote>
<p>-shared选项指明生成共享目标文件,-W1(注意是小写L而不是一)指明传入链接器的参数,在此我们设定了该库的<strong>soname</strong>
为libctest.so.1,-o则指明了生成的目标库文件为libctest.so.1.0(这个就是<strong>real name</strong>
)。</p>
<p>最后创建所需的符号链接:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo mv libctest.so.1.0 /usr/local/lib/libctest.so.1.0
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so.1
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ln -sf /usr/local/lib/libctest.so.1.0 /usr/local/lib/libctest.so</p>
</blockquote>
<p><strong>创建的libctest.so就是上面所谓linker name,用于编译时-lctest选项。</strong>
</p>
<p><strong>创建的libctest.so.1就是soname,我们在上边说过程序在运行时需要这个名字的符号链接。</strong>
</p>
<p>此时我们的共享库就建好了,接着我们编译程序:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ gcc -Wall -L/usr/local/lib prog.c -lctest -o prog
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ls
<br>
ctest1.c ctest1.o ctest2.c ctest2.o prog prog.c prog.o</p>
</blockquote>
<p>我们编译完毕,该库并不会包含在可执行文件中,只有在执行时来会动态加载进来。我们可以通过ldd列出一个可执行程序所有的依赖,在我的系统中还找不到/usr/local/bin的路径:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog
<br>
linux-gate.so.1 => (0x00a5c000)
<br>
libctest.so.1 => not found
<br>
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00a6f000)
<br>
/lib/ld-linux.so.2 (0x00451000)</p>
</blockquote>
<p>此时,运行会报找不到库的错误:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog
<br>
./prog: error while loading shared libraries: libctest.so.1: cannot open shared object file: No such file or directory</p>
</blockquote>
<p>我们可以将所需库的路径加入到系统路径中,有三种方法可以完成:</p>
<ul>
<li>A.在/etc/ld.so.conf中加入所在路径,然后执行ldconfig配置链接器运行时绑定配置。你也可以创建一个文件,将路径写入,然后使用ldconfig –f filename将配置写入。 </li>
<li>B.修改<tt>LD_LIBRARY_PATH</tt>
环境变量(Linux下,AIX下为LIBPATH),在其中添加路径。若你直接在.bashrc文件中配置则重启后不失效,否则在shell中设置重启后失效。 </li>
</ul>
<p>我们使用A方法中的-f选项:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ vi libctest.conf
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ sudo ldconfig -f libctest.conf
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ./prog
<br>
Valx=5
<br>
gnuhpc@gnuhpc-desktop:~/MyCode/lib/shared$ ldd prog
<br>
linux-gate.so.1 => (0x00f6f000)
<br>
libctest.so.1 => /usr/local/lib/libctest.so.1 (0x005d9000)
<br>
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00718000)
<br>
/lib/ld-linux.so.2 (0x001e6000)</p>
</blockquote>
<p>其中libctest.conf中写入路径:/usr/local/lib。程序运行正常。</p>
<p><strong>4.动态加载库的使用</strong>
</p>
<p>动态加载库是在非程序启动时动态加载进入程序的库,这对于实现插件或动态模块有很大的帮助。在Linux中,动态加载库的形式并不特殊,它使用上述两种程序库,使用提供的API在程序运行时动态加载。注意,在不同平台上动态加载库的API并不相同,所以可能会有移植问题出现。</p>
<p>我们可以通过nm命令先查看一下我们创建的库里面有哪些symbol(可以理解为函数方法)供我们使用:</p>
<blockquote>
<p>gnuhpc@gnuhpc-desktop:~/MyCode/lib$ nm /usr/local/lib/libctest.so
<br>
00001f18 a _DYNAMIC
<br>
00001ff4 a _GLOBAL_OFFSET_TABLE_
<br>
w _Jv_RegisterClasses
<br>
00001f08 d __CTOR_END__
<br>
00001f04 d __CTOR_LIST__
<br>
00001f10 d __DTOR_END__
<br>
00001f0c d __DTOR_LIST__
<br>
000005a0 r __FRAME_END__
<br>
00001f14 d __JCR_END__
<br>
00001f14 d __JCR_LIST__
<br>
00002014 A __bss_start
<br>
w __cxa_finalize@@GLIBC_2.1.3
<br>
00000540 t __do_global_ctors_aux
<br>
00000420 t __do_global_dtors_aux
<br>
00002010 d __dso_handle
<br>
w __gmon_start__
<br>
000004d7 t __i686.get_pc_thunk.bx
<br>
00002014 A _edata
<br>
0000201c A _end
<br>
00000578 T _fini
<br>
000003a0 T _init
<br>
00002014 b completed.7021
<br>
000004dc T ctest1
<br>
000004ec T ctest2
<br>
00002018 b dtor_idx.7023
<br>
000004a0 t frame_dummy
<br>
000004fc T main
<br>
U printf@@GLIBC_2.0</p>
</blockquote>
<p>这个命令对静态库和共享库都支持,第二列为symbol类型,小写字母表示符号是本地的,大写字母表示符号是全局(外部)的,几个常见的字母含义如下:T为代码段普通定义,D为已初始化数据段,B为未初始化数据段,U为未定义(用到该符号但是没有在该库中定义)。</p>
<p>我们创建<tt>ctest.h:</tt>
</p>
<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #ifndef CTEST_H
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: #define CTEST_H
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: #ifdef __cplusplus
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">extern</span>
"<span style="color: #8b0000;">C</span>
" {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: #endif
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: <span style="color: #0000ff;">void</span>
ctest1(<span style="color: #0000ff;">int</span>
*);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: <span style="color: #0000ff;">void</span>
ctest2(<span style="color: #0000ff;">int</span>
*);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11: #ifdef __cplusplus
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: }
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: #endif
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 14:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 15: #endif</pre>
</pre>
<p>这里使用extern C是为了使得该库既可以用于C语言又可以用于C++。</p>
<p>我们动态加载库进来:progdl.c</p>
<pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: #include
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: #include
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: #include "<span style="color: #8b0000;">ctest.h</span>
"
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: <span style="color: #0000ff;">int</span>
main(<span style="color: #0000ff;">int</span>
argc, <span style="color: #0000ff;">char</span>
**argv)
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: <span style="color: #0000ff;">void</span>
*lib_handle;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 8: <span style="color: #0000ff;">double</span>
(*fn)(<span style="color: #0000ff;">int</span>
*);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 9: <span style="color: #0000ff;">int</span>
x;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 10: <span style="color: #0000ff;">char</span>
*error;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 11:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 12: lib_handle = dlopen("<span style="color: #8b0000;">/usr/local/lib/libctest.so</span>
", RTLD_LAZY);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 13: <span style="color: #0000ff;">if</span>
(!lib_handle)
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 14: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 15: fprintf(stderr, "<span style="color: #8b0000;">%s/n</span>
", dlerror());
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 16: exit(1);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 17: }
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 18:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 19: fn = dlsym(lib_handle, "<span style="color: #8b0000;">ctest1</span>
");
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 20: <span style="color: #0000ff;">if</span>
((error = dlerror()) != NULL)
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 21: {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 22: fprintf(stderr, "<span style="color: #8b0000;">%s/n</span>
", error);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 23: exit(1);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 24: }
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 25:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 26: (*fn)(&x);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 27: printf("<span style="color: #8b0000;">Valx=%d/n</span>
",x);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 28:
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 29: dlclose(lib_handle);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 30: <span style="color: #0000ff;">return</span>
0;
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 31: }</pre>
</pre>
<p>里面的方法解释如下:</p>
<ul>
<li>void * dlopen(const char *filename, int flag); <br>
若filename为绝对路
径,那么dlopen就会试图打开它而不搜索相关路径,否则就现在环境变量LD_LIBRARY_PATH处搜索,然后在/etc
/ld.so.cache以及/lib和/usr/lib搜索。flag我们只解释两个常用的选项:若为RTLD_LAZY则表示在动态库执行时解决未定
义符号问题,而RTLD_NOW则表示在dlopen返回前解决未定义符号问题。当你调试时你应该用RTLD_NOW,这个时候若存在未解决的引用程序还
可以继续进行。另外,RTLD_NOW选项可能会使打开库的这个操作稍微慢一点,但是以后寻找函数时就会快一点。注意,若程序库相互依赖则应该按依赖顺序
依次载入,比如X依赖Y,那么要先载入Y然后再载入X。返回的是一个句柄,若失败则返回null.
</li>
<li>char *dlerror(void);
<br>
报告任何上一次对加载库操作的错误。两次调用期间若有操作错误则第二次会报告, 否则第二次则返回null——它报告完错误就等待下一个错误的发生,上一次错误的情况一旦报告就不再提及。
</li>
<li>void *dlsym(void *handle, const char *symbol);
<br>
寻找对应symbol的函数方法,handle就是dlopen返回的句柄。一般如下使用:
<br><pre style="border: 1px solid #cecece; padding: 5px; background-color: #fbfbfb; width: 650px; overflow: auto;"><pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 1: dlerror(); <span style="color: #008000;">/* clear error code */</span>
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 2: s = (actual_type) dlsym(handle, symbol_being_searched_for);
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 3: <span style="color: #0000ff;">if</span>
((err = dlerror()) != NULL) {
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 4: <span style="color: #008000;">/* handle error, the symbol wasn't found */</span>
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 5: } <span style="color: #0000ff;">else</span>
{
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 6: <span style="color: #008000;">/* symbol found, its value is in s */</span>
</pre>
<pre style="background-color: #fbfbfb; margin: 0em; width: 100%; font-family: consolas,'Courier New',courier,monospace; font-size: 15px;"> 7: }</pre>
</pre>
</li>
<li>int dlclose(void *handle);
<br>
关闭一个动态加载库。当一个动态库被加载多次时,你需要用同样次数dlclose该动态库才可以deallocated.
</li>
</ul>
<p>我们编译该代码gcc -g -rdynamic -o progdl progdl.c -ldl,即可得到可执行文件(其中-g选项是为了gdb调试所用),其中的库为动态加载后又关闭的。我们使用gdb看一下代码:</p>
<blockquote>
<p>(gdb) b main
<br>
Breakpoint 1 at 0x804878d: file progdl.c, line 12.
<br>
(gdb) r
<br>
Starting program: /home/gnuhpc/MyCode/lib/dynamic/progdl </p>
<p>Breakpoint 1, main (argc=1, argv=0xbffff4a4) at progdl.c:12
<br>
12 lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY);
<br>
(gdb) f
<br>
#0 main (argc=1, argv=0xbffff4a4) at progdl.c:12
<br>
12 lib_handle = dlopen("/usr/local/lib/libctest.so", RTLD_LAZY);
<br>
(gdb) s
<br>
13 if (!lib_handle)
<br>
(gdb) n
<br>
19 fn = dlsym(lib_handle, "ctest1");
<br>
(gdb)
<br>
20 if ((error = dlerror()) != NULL) <br>
(gdb)
<br>
26 (*fn)(&x);
<br>
(gdb)
<br>
27 printf("Valx=%d/n",x);
<br>
(gdb) p x
<br>
$1 = 5
<br>
(gdb) p fn
<br>
$2 = (double (*)(int *)) 0x28c4dc </p>
</blockquote>
<p>可以看到fn获得了ctest1的地址。</p>