当前位置: 首页 > 科技观察

骚操作:用强弱符号做插件库

时间:2023-03-21 20:24:33 科技观察

本文转载自微信公众号《编程珠玑》,作者王守老师。转载本文请联系编程诸暨(ID:寿网先声)公众号。《什么是强符号和弱符号》中简单介绍了强弱符号,那么强弱符号的本质有什么用呢?还记得《什么是强符号和弱符号》中提到的连接原则吗?当存在强符号和弱符号时,选择使用强符号那么我们可以利用这个原则来做以下事情:定义为弱符号,如果是弱符号,则使用默认行为如果链接库并且它是一个强符号,然后使用外部定义的行为来实现类插件的功能。通俗地说:当没有插件,使用默认行为链接插件时,使用插件的功能原理和例子也很简单:如果符号地址为0,供外部引用到weaksymbols,表示没有外挂插件库。强符号,遵循默认流程。如果符号地址不为0,则表示链接了插件库,执行了插件库的函数。示例程序如下://来源:公众号【编程明珠】//作者:守望先生#include__attribute__((weak))voidmy_print();voidtest_print(){//如果是是强符号,说明链接外部插件,使用外部定义if(my_print){my_print();}else{//弱符号,按照默认逻辑printf("thisisweakprint\n");}}intmain(void){test_print();return0;}上面的test_print函数是一个弱符号,如果在别处没有定义也可以正常编译运行:$gcc-omainmain.c$./mainthisisweakprint观察可执行文件:$nmmaingrepmy_printwmy_print我们也可以使用nm命令知道test_print是弱符号,它前面的修饰符是W,代表weak。插件库前面的示例程序能不能运行,如何让它支持插件库?或者,如何让它支持外部插件功能?制作库(静态库或动态库制作,请参考《手把手教你制作静态库》)这里有一个静态库的例子://print_plugin.c#includevoidmy_print(){printf("thisispluginprint\n");}创建一个静态库:$gcc-cprint_plugin.c$ar-rcslibprint_plugin.aprint_plugin.o链接插件库现在重新编译主程序并使用插件库:$gcc-omainmain.c-L./-lprint_plugin$gcc-omainmain.c-L.-Wl,--whole-archive-lprint_plugin-Wl,--no-whole-archive$nmmain|grepmy_print000000000000067aTmy_print$./mainthisispluginprint需要注意的是,在链接插件库之前,需要添加:-Wl,--whole-archive该选项会链接插件库中的所有符号,否则,main.c中已经有my_print符号,不会链接进去,之后必须恢复该选项。最后我们通过nm命令可以看到my_print符号不再是W了。我也看到了最后一个:thisispluginprint的打印。它还实现了我们所谓的插件的功能,换句话说,它可以削减或增加目标程序的功能。总结由于以下原因,我们可以自己制作一些支持插件库的程序:1.当存在重复的强弱符号时,使用强符号2.当不存在弱符号链接时,不会报错3.unlinkedexternalsymbols,地址为0,结合前面的例子判断解释,可以避免访问到非法地址:1.这一点在文章?2.初始程序中,即使没有链接插件库,程序也可以正常编译链接,不报错。3、当没有链接插件库时,由于其函数地址为0,我们在程序中判断,if(xxx),当地址为0时,执行默认行为语句。