2011-11-30 71 views
4

我的問題與this有一點相似,但它與TCL擴展有關。互相依賴的TCL擴展

我在Linux(gcc)上使用C,我有一個包含三個模塊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):

[email protected]:/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) 

[email protected]:/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不加載。

[email protected]:/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) 

[email protected]:/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

+1:關於複雜話題的好問題。 –

回答

6

Tcl的load命令使用dlopen()(在Linux上;當然在其他平臺上有所不同),它使用RTLD_LOCAL標誌;庫中的符號是而不是導出到應用程序的其餘部分。正因爲如此,一個動態加載的庫中的未綁定符號將無法解析另一個;這會促進隔離,但是會迫使你做更多的工作,讓所有的東西都能正確地運行在你想要實現這種依賴的地方。

的選項有:

  1. 如果libscoutIFIP.so取決於libbiddy.so的符號,建庫和動態鏈接器引擎將它全部整理出來,這樣的依賴沒有得到的時候告訴這給連接器加載多次。也就是說,如果一個庫依賴於另一個庫中的符號,它應該明確地將該庫列爲依賴項。
  2. 安排libbiddy.so通過Tcl的包API(Tcl_PkgProvide())將其符號導出爲存根表(即指向函數/變量的指針結構)。然後libscoutIFIP.sobiddy包上的Tcl_PkgRequireEx(),它將得到一個指向該存根表的指針,並且可以使用其中的引用而不是直接鏈接。這就是Tcl的存根機制的工作原理,其可怕的便攜式可讓您執行相當複雜的API版本管理(如有必要)。但要做更多的工作。 Tcler's Wiki在這個主題上有更深入的瞭解。

如果選項1適用於您,請與此一起;對於特定於Linux的代碼,這應該很好,因爲系統動態鏈接器不是絕對密集的(與Windows上的情況不同)。


[編輯]:需要注意的是舊版本的Tcl(高達8.5.9)使用RTLD_GLOBAL代替。看來這個更改應該在發行說明中標記爲***POTENTIAL INCOMPATIBILITY***,並且更加廣泛。代表Tcl開發人員道歉。

+0

Tcl的存根上的+1很棒。特別是當與該領域的Pythons產品進行比較時(http://www.python.org/dev/peps/pep-0384/)。 – schlenk

+0

您沒有解釋爲什麼使用tcl 8.5.8的系統在同一個軟件包上運行,而在tcl 8.5.10系統下無法運行。加載擴展時,8.5.8是否使用了'RTLD_GLOBAL'? –

+0

@Employed:是的,由於[Bug#3216070](https://sourceforge.net/tracker/?func=detail&aid=3216070&group_id=10894&atid=110894)。與該錯誤相關的[討論](https://sourceforge.net/mailarchive/message.php?msg_id=27211924)的總結是沒有選擇是完美的 - 如果有一個正確的選擇,我們會去那 - 但是RTLD_LOCAL保持不完整性更加本地化。很抱歉,更改導致了您的問題。 –