运行时库linux,Linux下一种运行时切换动态库的方法-Go语言中文社区

概述

假设有这样一种应用场景,有一个/lib/libfoo.so动态库,有两个或多个厂家各自实现了自己的版本,每个版本都不是尽善尽美,分别有自己的优势和缺点。可能app1使用v1版本的库比较合适、app2使用v2版本的库不会出bug等等。

在不能修改应用程序和动态库的前提下,本文提供了一种可以简单有效在运行时切换动态库的方法。

示例

v1版本库代码,v1/foo.c:#include

void foo1(void)

{

printf("This is v1 foo1n");

}

void foo2(void)

{

printf("This is v1 foo2n");

}

v2版本库代码,v2/foo.c:#include

void foo1(void)

{

printf("This is v2 foo1n");

}

void foo2(void)

{

printf("This is v2 foo2n");

}

两个版本都实现了foo1、foo2函数,分别打印自己的版本号和函数名。

用于切换的库代码,./gate.c:#include

#include

#include

static void gate(int argc, char *argv[], char *envp[])

{

if (strstr(argv[0], "v1")) {

dlopen("v1/libfoo.so", RTLD_GLOBAL | RTLD_NOW);

} else {

dlopen("v2/libfoo.so", RTLD_GLOBAL | RTLD_NOW);

}

}

static void *array[] __attribute__((section(".init_array"))) = {&gate};

gate.c实现了切换代码,切换条件为当app名称包含v1字符串时,切换到v1/libfoo.so。当app名称包含v2字符串时,切换到v2/libfoo.so。

实际应用时可以根据现实情况进行判断切换。

测试app代码,./test.c:#include

extern void foo1(void);

extern void foo2(void);

void main(void)

{

void (*func)();

void *handle;

foo1();

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

func = dlsym(handle, "foo2");

func();

}

分别测试了直接调用和通过dlsym调用的情况。

Makefile:all:

gcc -shared v1/foo.c -o v1/libfoo.so

gcc -shared v2/foo.c -o v2/libfoo.so

gcc -shared gate.c -o libfoo.so -ldl

gcc test.c -o v1.out -lfoo -ldl -L./v1

ln -sf v1.out v2.out

v2.out是指向v1.out的软链接,只有文件名不一样。

目录结构:root@debian:~# ll *

-rw-r–r-- 1 root root 335 Dec 25 13:07 gate.c

-rwxr-xr-x 1 root root 4772 Dec 25 12:59 libfoo.so

-rw-r–r-- 1 root root 181 Dec 25 13:20 Makefile

-rw-r–r-- 1 root root 216 Dec 25 13:08 test.c

-rwxr-xr-x 1 root root 5496 Dec 25 12:59 v1.out

lrwxrwxrwx 1 root root 6 Dec 25 12:59 v2.out -> v1.out

v1:

total 12

-rw-r–r-- 1 root root 121 Dec 25 12:54 foo.c

-rwxr-xr-x 1 root root 4688 Dec 25 12:59 libfoo.so

v2:

total 12

-rw-r–r-- 1 root root 121 Dec 25 12:54 foo.c

-rwxr-xr-x 1 root root 4688 Dec 25 12:59 libfoo.so

运行结果root@debian:~# ldd v1.out

linux-gate.so.1 (0xb77ac000)

libfoo.so => ./libfoo.so (0xb77a5000)

libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb7796000)

libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7621000)

/lib/ld-linux.so.2 (0x8005e000)

root@debian:~# ldd v2.out

linux-gate.so.1 (0xb77bc000)

libfoo.so => ./libfoo.so (0xb77b5000)

libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb77a6000)

libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7631000)

/lib/ld-linux.so.2 (0x8005b000)

root@debian:~# ./v1.out

This is v1 foo1

This is v1 foo2

root@debian:~# ./v2.out

This is v2 foo1

This is v2 foo2

原理分析

动态库加载时,不管是dlopen主动加载,还是被应用程序或其它动态库被动关联加载,都会执行动态库的初始化函数。该动作给了一个运行时动态切换运行库的契机。

ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY];

ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ];

addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr);

for (cnt = 0; cnt < i; ++cnt)

((init_t) addrs[cnt]) (argc, argv, env);

if (l->l_info[DT_INIT] != NULL)

DL_CALL_DT_INIT(l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr, argc, argv, env);

ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY];

addrs = (ElfW(Addr) *) (init_array->d_un.d_ptr + l->l_addr);

for (j = 0; j < jm; ++j)

((init_t) addrs[j]) (argc, argv, env);

加载时分别执行PREINIT_ARRAY函数数组、INIT函数、INIT_ARRAY函数数组。gate.c将初始化函数放在".init_array"段中,动态库加载时自动运行。

看来哪里的程序员都差不多,脾气不怎么好,直接代码里吐槽:

17ce9e04e95b14a5a1beb8ef8ffa03c6.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值