Каковы детали привязки динамических символов к 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, он не будет выгружен.