当有强符号和弱符号时,选择使用强符号
那么我们正可以利用这个原则做以下事情:
定义为弱符号,如果是弱符号,使用默认行为
如果链接了库,是强符号,则使用外部定义行为
以此来实现一个类似插件的功能。通俗一点说:
当没有插件时,使用默认行为
链接了插件时,使用插件的功能
原理和示例
其原理也非常简单:
外部引用弱符号
如果符号地址为0,则说明外部没有链接插件库,未有强符号,走默认流程
如果符号地址不为0,则说明链接了插件库,执行插件库的功能。
示例程序如下:
#include
__attribute__((weak)) void my_print();void test_print()
{// 如果是强符号,说明链接了外部插件,使用外部定义if(my_print){my_print();}else{// 弱符号,走默认逻辑printf("this is weak print\n");}
}
int main(void)
{test_print();return 0;
}
上面的test_print函数是弱符号,在没有其他地方定义的情况下,也是能够正常编译运行的:
$ gcc -o main main.c
$ ./main
this is weak print
观察可执行文件:
$ nm main |grep my_printw my_print
通过nm命令我们也可以知道test_print是弱符号,它前面的修饰字符是W,代表weak。
插件库
前面的示例程序已经能否工作了,如何让它能否支持插件库呢?或者说,如何让它支持外部的插件功能呢?
这里以静态库为例:
// print_plugin.c
#include
void my_print()
{printf("this is plugin print\n");
}
制作静态库:
$ gcc -c print_plugin.c
$ ar -rcs libprint_plugin.a print_plugin.o
链接插件库
现在重新编译main程序,并使用插件库:
$ gcc -o main main.c -L./ -lprint_plugin
$ gcc -o main main.c -L. -Wl,--whole-archive -lprint_plugin -Wl,--no-whole-archive
$ nm main |grep my_print
000000000000067a T my_print
$ ./main
this is plugin print
需要注意的是,这里在链接插件库之前,需要加上:
-Wl,--whole-archive
该选项会将插件库中所有符号都链接进来,若非如此,在main.c中已经有了my_print符号,将不会链接进来,而在此之后,又要将该选项恢复。最终我们可以通过nm命令看到my_print符号已经不再是W了。也就看到了最后:
this is plugin print
的打印了。
也就实现了我们所谓插件的功能,换句话说,可以对目标程序进行功能的裁剪或者增加。
总结
由于以下几点原因,我们可以自己做一些支持插件库的程序:
1.重复强弱符号同存在时,使用强符号
2.弱符号链接不存在时,不会报错
3.未链接的外部符号,地址为0,可通过判断避免访问非法地址
再结合前面的例子分别解释一下:
1.在开始的程序中,即便没有链接插件库,程序也可以正常编译链接通过,而不会报错
2.没有链接插件库时,由于其函数地址为0,因此,我们程序内判断,if(xxx),当地址为0时,执行默认的行为语句。
声明:
本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。