Каковы детали привязки динамического символа в OS X?

У меня действительно странная ситуация с динамической привязкой символов в OS X, и я надеюсь получить некоторые подсказки о том, как ее решить.

У меня есть приложение, написанное на C, которое использует dlopen() для динамической загрузки модулей во время выполнения. Некоторые из этих модулей экспортируют глобальные символы, которые могут использоваться другими модулями, загруженными позже.

У нас есть один модуль (назовем его weird_module.so), который экспортирует глобальные символы, одним из которых является weird_module_function. Если странный_модуль.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!

Изменить:

Я попытался собрать минимальное приложение, чтобы воспроизвести проблему, и в ходе этого, по крайней мере, понял, что мы делаем неправильно. Есть еще два важных факта, имеющих отношение к дублированию проблемы.

Во-первых, 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 для глобального const происходит столкновение символов.

$ nm weird_module.so | grep '_something_global`
00000000000158f0 S _something_global

$ nm libsomething.dylib | grep '_something_global'
0000000000031130 S _something_global

Я готов считать, что повторяющийся символ поставит меня в область неопределенного поведения, поэтому я опускаю вопрос.


person leedm777    schedule 09.09.2013    source источник


Ответы (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, чтобы узнать, какие конструкторы вызываются.

Еще несколько вещей, которые нужно проверить:

  1. Вы на 100% уверены, что оба модуля переоткрыты с помощью RTLD_LAZY | RTLD_GLOBAL? Единственный способ получить ошибки dyld — передать параметр RTLD_LOCAL.
  2. Вы уверены, что вызов dlclose выполнен успешно (возвращает 0)? Если, например, ваш модуль содержит код Objective-C, он не будет выгружен.
person 0xced    schedule 17.12.2013