我的问题与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:为什么它不适用于新系统?
答案 0 :(得分:6)
Tcl的load
命令使用dlopen()
封面(在Linux上;当然在其他平台上有所不同)并且它使用RTLD_LOCAL
标志;库中的符号不导出到应用程序的其余部分。因此,一个动态加载的库中的未绑定符号将无法解析另一个;这会增加隔离,但会迫使你做更多的工作,使你的所有功能都能正确地存在,而你想要这样的依赖实际存在。
您的选择是:
libscoutIFIP.so
依赖于libbiddy.so
的符号,请在构建库时告诉链接器,动态链接器引擎会将其全部排序,以便不会多次加载依赖项。也就是说,如果库依赖于另一个库中的符号,则它应该明确地将该库列为依赖项。libbiddy.so
通过Tcl的包API(Tcl_PkgProvide()
)将其符号导出为存根表(即指向函数/变量的指针结构)。然后当libscoutIFIP.so
在Tcl_PkgRequireEx()
包上执行biddy
时,它将获得指向该存根表的指针,并且可以使用其中的引用而不是直接链接。这就是Tcl的存根机制的工作原理,它非常棒的和 portable 和可以让你进行相当复杂的API版本管理(如果需要)。尽管如此,设置还需要做多少工作。 Tcler's Wiki在这个主题上进行了更深入的探讨。如果选项1适合你,那就去吧;对于特定于Linux的代码应该没问题,因为系统动态链接器并不是非常密集(与Windows上的情况不同)。
[编辑]:请注意旧版本的Tcl(最高8.5.9)使用RTLD_GLOBAL
代替。似乎这个更改应该在发行说明中标记为***POTENTIAL INCOMPATIBILITY***
,并且更广泛地落后。代表Tcl开发人员道歉。