共享库的动态加载/卸载

这份文档来自<<Advanced Linux
Programming>>,解释如何动态加载共享库,使用这

种技术你可以在程序中精确控制加载某个共享库。本文同时讨论了共享库中符号解析

问题。



dlopen( "libtest.so", RTLD_LAZY );



这个调用将打开共享库libtest.so,第二形参通常都是RTLD_LAZY。为了使用dlopen

函数,需要包含<dlfcn.h>头文件,指定链接开关-ldl。



假设libtest.so中定义了函数my_function



--------------------------------------------------------------------------

void *handle;

void ( *test ) ( void );



handle = dlopen( "libtest.so", RTLD_LAZY );

test = dlsym( handle, "my_function" );

test();

dlclose( handle );

--------------------------------------------------------------------------



dlsym()系统调用还可用于获取指向共享库中某个静态变量的指针。dlopen与dlsym调

用失败时均返回NULL,此时可以调用dlerror(没有形参)获取相应的可读错误信息。



dlclose函数用于卸载共享库。技术上,dlopen只在一个共享库尚未被加载的情况下

真正加载它。如果一个共享库已经被加载了,dlopen简单地递增该共享库引用计数。

类似的,dlclose递减共享库引用计数,当引用计数达到零时卸载共享库。



如果你是用C++编写共享库,而又想用dlsym访问其中的函数、静态变量,可能需要这

样定义:



--------------------------------------------------------------------------

#ifdef __cplusplus

extern "C"

{

#endif



static char my_char;

static void my_funtion ( void );



#ifdef __cplusplus

}

#endif

--------------------------------------------------------------------------



这使得C++编译器保持my_char、my_funtion的原始名字。对于C编译器不存在该问题。



一个共享库可能会引用外部定义的函数和变量。假设你用dlopen打开这样一个共享库。

如果指定了RTLD_LAZY,Linux不会在立即解析未定义的符号名,直到共享库中代码第

一次试图引用未定义的符号名,Linux才开始解析它。如果解析成功,程序继续执行,

反之显示错误信息并终止程序。如果指定了RTLD_NOW,Linux会在调用dlopen时立即

解析未定义的符号名。解析失败时dlopen返回NULL。



那么Linux根据什么解析未定义的符号名呢,有这么几种情况:



如果主程序引出(exports)任意动态符号(共享库正是这样做的),则这些符号自然可

用。然而缺省情况下,常规可执行程序不会以动态符号形式引出它们的函数和变量名。

为了达到这个效果,可以在编译时指定"-Wl,-export-dynamic",这实际是链接选项。



如果主程序编译时选择了动态链接,则dlopen打开的库可以引用编译时共享库中的符

号。



如果主程序使用dlopen打开共享库A、B,缺省情况下A与B彼此不能引用对方定义的动

态符号。但是可以在dlopen第二形参上逻辑或一个RTLD_GLOBAL标志,使得相应共享

库中动态符号全局可见。



--------------------------------------------------------------------------

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <dlfcn.h>



int main ( int argc, char * argv[] )

{

void *handle;

void ( *foo ) ( void );



handle = dlopen( "libfoo.so", RTLD_LAZY );

foo = dlsym( handle, "foo" );

foo();

dlclose( handle );

return( EXIT_SUCCESS );

} /* end of main */

--------------------------------------------------------------------------



假设foo()调用了另一个函数bar(),而bar()不是libfoo.so中定义的,怎么办。



一种办法是在你的主程序里包含bar()函数体,然后指定"-Wl,-export-dynamic"编译

主程序:



$ cc -Wl,-export-dynamic -o main main.c bar.c -ldl



第二种办法是将bar()放到单独一个共享库里,编译时动态链接进主程序



$ cc -fPIC -shared -o libbar.so bar.c

$ cc -o main main.c libbar.so -ldl



第三种办法是将bar()放到单独一个共享库里,运行时由main函数调用dlopen动态加

载。注意,dlopen的第二形参中必须逻辑或RTLD_GLOBAL标志,否则libfoo.so看不到

libbar.so引出的动态符号。



--------------------------------------------------------------------------

void *bar_handle;



bar_handle = dlopen( "libbar.so", RTLD_LAZY | RTLD_GLOBAL );

--------------------------------------------------------------------------



Q: 我试图用dlopen()打开一个共享库,该共享库使用了一个extern型变量,后者位

于主调二进制文件中。但是这个dlopen()调用失败了,报告存在无法解析的外部

符号。我用的是gcc(Cygnus version 2.9),怎么解决这个问题。



--------------------------------------------------------------------------

/*

* Library testlib.c

* gcc -Wall -pipe -O3 -o testlib.o -c testlib.c

* /usr/ccs/bin/ld -G testlib.o -o testlib.so

*/

extern int abc;



void testing ( void )

{

abc = 0;



return;

}



/*

* Main testcall.c

* gcc -Wall -pipe -O3 -o testcall testcall.c -ldl

*/

#include <stdio.h>

#include <stdlib.h>

#include <ctype.h>

#include <unistd.h>

#include <dlfcn.h>



int abc = -1;



int main ( void )

{

void *testlib;

void ( *testing_call ) ();



fprintf( stderr, "abc = %d/n", abc );

if ( ( testlib = dlopen( "./testlib.so", RTLD_NOW | RTLD_GLOBAL ) ) != NULL )

{

testing_call = dlsym( testlib, "testing" );

( *testing_call )();

fprintf( stderr, "abc = %d/n", abc );

}

return( EXIT_SUCCESS );

}

--------------------------------------------------------------------------



A: flyriver 2001-12-16 22:48



Linux系统中编译主调程序时加上"-rdynamic"参数



Q: 在libfoo.so中实现了func1()、func2(),其中func2()调用了func1()。主调程序

prog.c中包含一个同名函数func1(),但与libfoo.so中func1()有不同形参类型。

prog.c使用dlopen()打开libfoo.so,并调用其中的func2()。我期望此时func2()

仍去调用libfoo.so中的func1(),但现在事实是调用了prog.c中的func1(),怎么

办。



A: Paul Pluzhnikov <ppluzhnikov@earthlink.net> 2003年5月31日 13:05



对于Win32、AIX来说,你所期待的效果是缺省行为,但其它操作系统未必如此。对于

Solaris、FreeBSD、Linux,使用gnu ld生成libfoo.so时应指定-Bsymbolic。缺省情

况下没有指定-Bsymbolic,此时prog.c中的func1()被func2()调用。



A: Mars Rullgard <mru@users.sourceforge.net> 2003年5月30日 17:09



如果libfoo.so没有使用prog.c中符号(变量、函数),使用gnu ld链接prog.c时不要

指定-rdynamic或者-export-dynamic,这样prog.c中func1符号不被引出,func2()就

不会调用func1()。如果还是不能解决问题,设法使func1这个符号成为weak symbol,

假设你正在使用gcc,可以这样做:



int __attribute__((weak)) func1 ( ... )

{

... ...

}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值