Linux动态链接之五:运行时显式加载共享文件.so

动态链接更多是从.lib等模块分离组装的角度来看待问题,实际上,如果动态链接可以实现,那么在运行过程中动态加载.so对象也是可行的,这种共享对象被称为DLL,它其实和普通.so对象并无本质区别,只是程序的视角不同。

运行时链接的技术手段常用来做插件、驱动加载等处理,因为不需要开始就全部加载进入进程,可以有效地减少程序启动时间和内存使用。对于Web服务器需要长期连续运行的场景,如果某个模块更新,显然不能重启再加载,故而这种情况下使用DLL手段是合理。

动态链接是由动态链接器.ld.so全程负责,对于程序是透明的,而运行时加载,同样是由程序借助.ld.so封装提供的4个API来完成操作的:
1. dlopen()
void * dlopen(const char* filename, int flag);
打开一个动态库,并将其加载到进程的地址空间,完成初始化过程。第一个参数是动态库的路径,如果是相对路径比如只提供文件名,那么则按照LD_LIBRARY_PATH,/etc/ld.so.cache, /lib, /usr/lib等系统路径顺序查找匹配的共享库。如果filename=0则会返回当前可执行文件的全局符号表的句柄,则可以在运行时定义任何符号的位置,并且可以执行它们,类似高级语言的“反射”机制。

flag可选RTLD_LAZY(PLT延迟绑定机制);RTLD_NOW表示模块被加载时立即完成所有符号的绑定工作,如果有任何未定义的符号引用,则返回错误。在调试程序时应该使用RTLD_NOW作为参数,因为有符号未定义引用的情况可以立即定位,而使用RTLD_LAZY延迟绑定则会导致绑定工作推迟,并且难以捕获。

dlopen()返回的相应模块的句柄,这个句柄用来在后续dlsym()等函数中用来定位模块内部内容,如果加载模块失败,则返回NULL。

2.dlsym()

void* dlsym(void* handle, char* symbol)

运行时装载查找指定符号,如果查到则返回符号的地址或者常量值,如果没有查到,则返回NULL,同时dlerror()返回相应错误信息。

3.dlerror()
每次调用dlopen()\dlsym()等函数后,可以通过dlerror(0函数来判断上次调用是否成功。dlerror()函数返回值类型为char*, 如果dlerror()返回的是NULL则标识成功,如果不是,则返回相应错误信息。

4.dlclose()
dlclose()函数作用和dlopen()相反,输入相应模块的句柄,用来从虚拟进程空间中卸载相应的模块。并在卸载之前,先执行.finit段的代码,然后将各模块的符号从全局符号表中去除掉,取消进程空间跟模块的映射关系,然后才是关闭模块文件的读取通道。

#include <stdio.h>
#include <dlfcn.h>

/*调用规范:
$RunSo /lib/foobar.so  function arg1 arg2 ... return_type
$./runso /lib/i386-linux-gnu/libm-2.xx.so sin d2.0 d
*/

#define SETUP_STACK \
i = 2;              \
while(++i < argc-1) {                             \
    switch(argv[i][0]) {                      \
        case 'i':                         \
            asm volatile("push %0" :: "r"(atoi (&argv[i][1]))); \
            esp += 4;  \
            break;     \
        case 'd':\
                        atof(&argv[i][1]); \
            asm volatile("subl $8, %esp\n" "fstpl (%esp)" ); \
            esp += 8; \
            break;    \
        case 's':  \
                        asm volatile("push %0" :: "r"(&argv[i][1]) ); \
            esp += 4; \ break;    \
        default:          \
            printf("error argument type"); \
            goto exit_runso;  \
        } }                    

#define RESTORE_STACK   asm volatile("add %0, %%esp"::"r"(esp))

int main(int argc, char* argv[]) {
    void* handle;
    char* error;
    int i;
    int esp = 0;
    void* func;

    handle = dlopen(argv[1], RTLD_NOW);
    if(handle == 0) {
        printf("Can't find library:%s\n", argv[1]);
        return -1;
    }

    func = dlsym(handle, argv[2]);
    if ( (error = dlerror()) != NULL){
        printf("Find symbol %s error: %s\n", argv[2], error);
        goto exit_runso;
    }

    switch (argv[argc-1][0]){
        case 'i':{
            int (*func_int)() = func;
            SETUP_STACK;
            int ret = func_int();
            RESTORE_STACK;
            printf("ret = %d\n", ret);
            break;
            }
        case 'd':{
            double (*func_double) () = func;
            SETUP_STACK;
            double ret = func_double();
            RESTORE_STACK;
            printf("ret = %f\n", ret);
            break;
            }
        case 's':{
            char* (*func_str)() = func;
            SETUP_STACK;
            char* ret = func_str();
            RESTORE_STACK;
            printf("ret = %s\n", ret);
            break;
            }
        case 'v':{
            void (*func_void)() = func;
            SETUP_STACK;
            func_void();
            RESTORE_STACK;
            printf("ret = void");
            break;
            }
        } //end of switch

        exit_runso:
        dlclose(handle);
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值