Расширения TCL, которые зависят друг от друга - PullRequest
4 голосов
/ 30 ноября 2011

Мой вопрос немного похож на this , но он касается расширений TCL.

Я использую C в Linux (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 не имеет пользовательских зависимостей. Хотя это странно, расширения ботов загружены и работают должным образом. Вот что у меня есть (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: почему он не работает на новой системе?

1 Ответ

6 голосов
/ 30 ноября 2011

Команда Tcl load использует dlopen() под прикрытием (в Linux; конечно, на других платформах она отличается) и использует ее с флагом RTLD_LOCAL; символы в библиотеке не экспортируются в остальную часть приложения. Из-за этого несвязанные символы в одной динамически загружаемой библиотеке не будут сопоставляться с другой; это повышает изоляцию, но заставляет вас делать больше работы, чтобы все работало правильно там, где вы хотите, чтобы такая зависимость действительно существовала.

Ваши варианты:

  1. Если libscoutIFIP.so зависит от символов libbiddy.so, сообщите об этом компоновщику при сборке библиотеки, и механизм динамического компоновщика все это уладит, чтобы зависимость не загружалась несколько раз. То есть, если библиотека зависит от символа в другой библиотеке, она должна явно указать эту библиотеку как зависимость.
  2. Организовать libbiddy.so для экспорта своих символов в виде таблицы-заглушки (т. Е. Структуры указателей на функции / переменные) через пакетный API Tcl (Tcl_PkgProvide()). Затем, когда libscoutIFIP.so делает Tcl_PkgRequireEx() в пакете biddy, он получит указатель на эту таблицу-заглушку и может использовать ссылки внутри нее вместо прямой ссылки. Вот как работает механизм-заглушка Tcl, и его замечательные и portable и позволяют вам выполнять довольно сложное управление версиями API (при необходимости). Это немного больше, чтобы настроить, хотя. Tcler's Wiki углубляется в эту тему.

Если вариант 1 работает для вас, продолжайте с этим; для специфичного для Linux кода это должно быть просто отлично, так как системный динамический компоновщик не слишком плотный (в отличие от ситуации в Windows).


[EDIT]: обратите внимание, что более старые версии Tcl (до 8.5.9) использовали вместо RTLD_GLOBAL. Похоже, что это изменение должно было быть помечено ***POTENTIAL INCOMPATIBILITY*** в примечаниях к выпуску и отслеживаться более широко. Извинения от имени разработчиков Tcl.

...