Каковы детали привязки динамических символов к OS X?
У меня очень странная ситуация с привязкой динамических символов в OS X, и я надеюсь получить некоторые подсказки о том, как ее решить.
У меня есть приложение, написанное на C, которое использует dlopen()
для динамической загрузки модулей во время выполнения. Некоторые из этих модулей экспортируют глобальные символы, которые могут использоваться другими модулями, загруженными позже.
У нас есть один модуль (который я назову weird_module.so
), который экспортирует глобальные символы, один из которых weird_module_function
. Если weird_module.so связывается с определенной библиотекой (которую я буду называть libsomething.dylib
), то weird_module_function
не может быть привязан к. Но если я удаляю -lsomething
при связывании weird_module.so
, тогда я могу привязываться к weird_module_function
.
Что может произойти с libsomething.dylib
, что приведет к тому, что weird_module.so
не будет экспортировать символы? Есть ли что-то, что я могу сделать, чтобы отлаживать, как экспортируются символы (подобно тому, как я могу использовать DYLD_PRINT_BINDINGS
для отладки, как они связаны)?
$ LDFLAGS="-bundle -mmacosx-version-min=10.6 -Xlinker -undefined -Xlinker dynamic_lookup /usr/lib/bundle1.o"
$ gcc -o weird_module.so ${LDFLAGS} weird_module.o -lsomething
$ nm weird_module.so | grep '_weird_module_function$'
00000000000026d0 T _weird_module_function
$ gcc -o other_module.so ${LDFLAGS} other_module.o -lsomething
$ nm other_module.so | grep '_weird_module_function$'
U _weird_module_function
$ run-app
Loading weird_module.so
Loading other_module.so
dyld: lazy symbol binding failed: Symbol not found: _weird_module_function
Referenced from: other_module.so
Expected in: flat namespace
dyld: Symbol not found: _weird_module_function
Referenced from: other_module.so
Expected in: flat namespace
# Now relink without -lsomething
$ gcc -o weird_module.so ${LDFLAGS} weird_module.o
$ nm weird_module.so | grep '_weird_module_function$'
00000000000026d0 T _weird_module_function
$ run-app
Loading weird_module.so
Loading other_module.so
# No error!
Edit:
Я попытался собрать минимальное приложение, чтобы дублировать проблему, и в ходе этого, по крайней мере, выяснил, что мы делали неправильно. Есть два других важных факта, связанных с дублированием проблемы.
Во-первых, run-app
предварительно загружает модуль с помощью RTLD_LAZY | RTLD_LOCAL
для проверки своих метаданных. Модуль затем dlclose()
ed и снова открывается либо RTLD_LAZY | RTLD_GLOBAL
, либо RTLD_NOW | RTLD_LOCAL
, в зависимости от метаданных. (Для обоих рассматриваемых модулей он снова открывается с помощью RTLD_LAZY | RTLD_GLOBAL
).
Во-вторых, оказывается, что это столкновение символов в weird_module.so
и libsomething.dylib
для a const
global.
$ nm weird_module.so | grep '_something_global`
00000000000158f0 S _something_global
$ nm libsomething.dylib | grep '_something_global'
0000000000031130 S _something_global
Я согласен с тем, что дубликат символа помещает меня в область поведения undefined, поэтому я оставляю вопрос.
Ответы
Ответ 1
Я попытался воспроизвести ваш сценарий, и я смог получить те же ошибки, что и вы, т.е. dyld: lazy symbol binding failed
, а затем dyld: Symbol not found
.
Но это не имело никакого отношения к привязке к libsomething.dylib
или нет. То, что я сделал, чтобы вызвать эту ошибку, было просто вызов weird_module_function()
из конструктора other_module.so
:
// other_module.c
#import <stdio.h>
#import "weird_module.h"
__attribute__((constructor)) void initialize_other_module(void)
{
printf("%s\n", __PRETTY_FUNCTION__);
weird_module_function();
}
Вот как я загрузил модули:
// main.c
#import <stdio.h>
#import <dlfcn.h>
int main(int argc, const char * argv[])
{
printf("\nLoading weird module\n");
void *weird = dlopen("weird_module.so", RTLD_LAZY | RTLD_LOCAL);
printf("weird: %p\n\n", weird);
printf("Loading other module\n");
void *other = dlopen("other_module.so", RTLD_LAZY | RTLD_LOCAL);
printf("other: %p\n", other);
return 0;
}
Ошибки dyld исчезают, если я удаляю параметр RTLD_LOCAL
при загрузке weird_module.so
.
Такая же ошибка возникает, если вы вызываете weird_module_function
из конструктора libsomething.dylib
, но это происходит до того, как вызывается main
, так что, вероятно, это не то, что происходит с вами.
Но, возможно, конструктор libsomething.dylib
должен искать, как libsomething.dylib
влияет на процесс загрузки модулей. Вы можете установить переменную среды DYLD_PRINT_INITIALIZERS
в YES
, чтобы узнать, какие конструкторы вызываются.
Несколько других вещей, которые нужно проверить:
- Вы на 100% уверены, что оба модуля снова открыты с помощью
RTLD_LAZY | RTLD_GLOBAL
? Единственный способ получить dyld-ошибки - передать параметр RTLD_LOCAL
.
- Вы уверены, что вызов
dlclose
успешный (возвращает 0)? Если, например, ваш модуль содержит код Objective-C, он не будет выгружен.