我的问题有点类似于this,但它是关于TCL扩展.
我在Linux上使用C(gcc),我有一个包含三个模块A,B和C的包.模块A包含函数,还定义(不仅仅是声明)全局变量.我编译并将模块A链接到动态库(libA.so).
现在,我希望B和C是TCL扩展.两者都使用来自A的函数和全局变量,而C也使用来自B的函数.我已经制作了B和C共享库(B.so和C.so)但没有使用“-Wl -soname”.我做B.so取决于A.so,而C.so没有用户依赖.虽然这很奇怪,但bot加载并正常工作.这是我所拥有的(A = libbiddy.so,B = bddscout.so,C = bddscoutIFIP.so):
meolic@meolic:/usr/lib/bddscout$ldd *.so
bddscout.so:
linux-gate.so.1 => (0x00177000)
libbiddy.so.1 => /usr/lib/libbiddy.so.1 (0x00eca000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00342000)
/lib/ld-linux.so.2 (0x0061f000)
bddscoutIFIP.so:
linux-gate.so.1 => (0x00fc2000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00c75000)
meolic@meolic:/usr/lib/bddscout$wish
% puts $tcl_patchLevel
8.5.8
% load ./bddscout.so
% load ./bddscoutIFIP.so
% info loaded
{./bddscoutIFIP.so Bddscoutifip} {./bddscout.so Bddscout} {{} Tk}
问题是,完全相同的包在任何地方都无法正常工作.在新的计算机扩展C.so不加载.
meolic@altair:/usr/lib/bddscout$ldd *.so
bddscout.so:
linux-gate.so.1 => (0xb76ef000)
libbiddy.so.1 => /usr/lib/libbiddy.so.1 (0xb76c9000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb754d000)
/lib/ld-linux.so.2 (0xb76f0000)
bddscoutIFIP.so:
linux-gate.so.1 => (0xb7780000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75e8000)
/lib/ld-linux.so.2 (0xb7781000)
meolic@altair:/usr/lib/bddscout$wish
% puts $tcl_patchLevel
8.5.10
% load ./bddscout.so
% load ./bddscoutIFIP.so
couldn't load file "./bddscoutIFIP.so": ./bddscoutIFIP.so: undefined symbol: biddy_termFalse
报告的未定义符号是来自A的全局变量之一.问题1:我的方法是否正确,因为它适用于某些系统?问题2:为什么它不适用于新系统?
解决方法:
Tcl的load命令使用了dlopen()(在Linux上;在其他平台上它当然不同)并且它与RTLD_LOCAL标志一起使用;库中的符号不会导出到应用程序的其余部分.因此,一个动态加载的库中的未绑定符号将无法解析另一个;这会增加隔离,但会迫使你做更多工作,使所有功能正常运行,你想要这样的依赖实际存在.
你的选择是:
>如果libscoutIFIP.so依赖于libbiddy.so的符号,请在构建库时将其告知链接器,动态链接器引擎会将其全部排序,以便不会多次加载依赖项.也就是说,如果库依赖于另一个库中的符号,它应该明确地将该库列为依赖项.
>安排libbiddy.so通过Tcl的包API(Tcl_PkgProvide())将其符号导出为存根表(即指向函数/变量的指针结构).然后,当libscoutIFIP.so对biddy包执行Tcl_PkgRequireEx()时,它将获得指向该存根表的指针,并且可以使用其中的引用而不是直接链接.这就是Tcl的存根机制的工作原理,它非常棒且可移植,并且允许您进行相当复杂的API版本管理(如果需要).尽管如此,设置还需要做多少工作. Tcler’s Wiki在这个主题上进一步深入.
如果选项1适合你,那就去吧;对于特定于Linux的代码应该很好,因为系统动态链接器并不是非常密集(与Windows上的情况不同).
[编辑]:请注意,旧版本的Tcl(最高8.5.9)使用了RTLD_GLOBAL.似乎这个变化应该在发行说明中被标记为***潜在的不兼容***并且落后更广泛.代表Tcl开发人员道歉.