Чем отличается дилиб от другого?

Я играю с несколькими загрузками dylib и пытаюсь понять, что отличает символы.

Вот мои шаги:

  • Создайте lib_a.dylib со следующей точкой входа:

    FactoryA : IFActory () {}
    extern "C" IFactory* GetFactory () { return new FactoryA(); }
    
  • Скопируйте lib_a.dylib в lib_b.dylib
  • Загрузите GetFactory из lib_a.dylib и lib_b.dylib

    void * module=dlopen(fileName,RTLD_LAZY);
    void * proc = (void *)dlsym(module, "GetFactory");
    

При загрузке второго dylib (lib_b.dylib) GetFactory считается уже определенным lib_a.dylib.
Фактически выход нм имеет тот же результат.

Но я думал, что тег компиляции -two_level_namespace гарантирует, что 2 dylib находятся в каком-то другом пространстве имен, я не прав?

Что я могу изменить, чтобы загрузить два моих dylib?


Ниже мой тест.

myclass.h:

#include <stdio.h>
class IFactory {
public:
virtual int GetCount() = 0;
};
extern "C"{
extern IFactory* GetFactory ();
}

myclass.cpp

#include <stdio.h>
#include "myclass.h"
class MyFactory : public IFactory {
public:
virtual int GetCount() { mCount++; return mCount; }
static int mCount;
};
int MyFactory::mCount = 0;IFactory* GetFactory () {
return new MyFactory;
}

mytest.cpp

#include <stdio.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include "myclass.h"
typedef IFactory* (*factoryPtr)();

int main()
{
void* handleA = dlopen("libmylib.dylib", RTLD_LAZY);
factoryPtr functionA = (IFactory*(*)())dlsym(handleA, "GetFactory");
IFactory*  factoryA = (*functionA)();
fprintf(stderr, "Handle A: %p\tFunction A: %p\t Count: %d\n", handleA, functionA, factoryA->GetCount());

// Reload same library
void* handleB = dlopen("libmylib.dylib", RTLD_LAZY);
factoryPtr functionB = (IFactory*(*)())dlsym(handleB, "GetFactory");
IFactory*  factoryB = (*functionB)();
fprintf(stderr, "Handle B: %p\tFunction B: %p\t Count: %d\n", handleB, functionB, factoryB->GetCount());

// Load copy of first library (just rename)
void* handleC = dlopen("libmylib_copy.dylib", RTLD_LAZY);
factoryPtr functionC = (IFactory*(*)())dlsym(handleC, "GetFactory");
IFactory*  factoryC = (*functionC)();
fprintf(stderr, "Handle C: %p\tFunction C: %p\t Count: %d\n", handleC, functionC, factoryC->GetCount());

return 0;
}

Команда:

clang++ -dynamiclib myclass.cpp -o libmylib.dylib
cp libmylib.dylib libmylib_copy.dylib
clang++ mytest.cpp -o mytest
./mytest

Выход :

Handle A: 0x7fe5dac039b0    Function A: 0x106d49d30  Count: 1
Handle B: 0x7fe5dac039b0    Function B: 0x106d49d30  Count: 2
Handle C: 0x7fe5dac03e00    Function C: 0x106d7cd30  Count: 3

Почему у нас есть счет = 3 в конце?


Атрибут -fvisibility = hidden -fvisibility-inlines-hidden позволяет делать то же самое.

Модифицированный myclass.h:

#include <stdio.h>
#define EXPORT_FACTORY  __attribute__ ((visibility ("default")))
class IFactory {
public:
virtual int GetCount() = 0;
};
extern "C"{
extern EXPORT_FACTORY IFactory* GetFactory ();
}

Сборка:

clang++ -dynamiclib myclass.cpp  -o libmylib.dylib  -fvisibility=hidden -fvisibility-inlines-hidden
cp libmylib.dylib libmylib_copy.dylib
clang++ mytest.cpp -o mytest
./mytest

Выход :

Handle A: 0x7fe078c039b0    Function A: 0x1076e1c00  Count: 1
Handle B: 0x7fe078c039b0    Function B: 0x1076e1c00  Count: 2
Handle C: 0x7fe078c03e20    Function C: 0x107714c00  Count: 1

2

Решение

Основной проблемой здесь является видимость символов в вашей библиотеке. Вы можете увидеть через nm -m что есть несколько внешних символов, кроме GetFactory функция, которую вы собираетесь экспортировать:

$ nm -m libmylib.dylib
0000000000000f30 (__TEXT,__text) external _GetFactory
0000000000001068 (__DATA,__common) external __ZN9MyFactory6mCountE
0000000000000f50 (__TEXT,__text) weak external __ZN9MyFactory8GetCountEv
0000000000001038 (__DATA,__data) weak external __ZTI8IFactory
0000000000001050 (__DATA,__data) weak external __ZTI9MyFactory
0000000000000f91 (__TEXT,__const) weak external __ZTS8IFactory
0000000000000f86 (__TEXT,__const) weak external __ZTS9MyFactory
0000000000001020 (__DATA,__data) weak external __ZTV9MyFactory
(undefined) external __ZTVN10__cxxabiv117__class_type_infoE (from libc++)
(undefined) external __ZTVN10__cxxabiv120__si_class_type_infoE (from libc++)
(undefined) weak external __Znwm (from libc++)
(undefined) external dyld_stub_binder (from libSystem)

Это символы, помеченные как weak external это причина вашей проблемы.

Запуск вашего тестового приложения с DYLD_PRINT_BINDINGS=YES показывает:

$ DYLD_PRINT_BINDINGS=YES ./mytest
[ … output showing initialization of libstdc++.dylib and libmylib.dylib omitted …]
Handle A: 0x7fc729c03810    Function A: 0x102a51ee0  Count: 1
Handle B: 0x7fc729c03810    Function B: 0x102a51ee0  Count: 2
dyld: bind: libmylib_copy.dylib:0x102A85038 = libc++abi.dylib:__ZTVN10__cxxabiv117__class_type_infoE, *0x102A85038 = 0x7FFF7CA67B50 + 16
dyld: bind: libmylib_copy.dylib:0x102A85050 = libc++abi.dylib:__ZTVN10__cxxabiv120__si_class_type_infoE, *0x102A85050 = 0x7FFF7CA67BD0 + 16
dyld: bind: libmylib_copy.dylib:0x102A85018 = libstdc++.6.dylib:__Znwm, *0x102A85018 = 0x7FFF938F0325
dyld: bind: libmylib_copy.dylib:0x102A85000 = libdyld.dylib:dyld_stub_binder, *0x102A85000 = 0x7FFF9084E878
dyld: weak bind: libmylib_copy.dylib:0x102A85030 = libmylib.dylib:__ZN9MyFactory8GetCountEv, *0x102A85030 = 0x102A51F00
dyld: weak bind: libmylib_copy.dylib:0x102A85060 = libmylib.dylib:__ZTI8IFactory, *0x102A85060 = 0x102A52038
dyld: weak bind: libmylib_copy.dylib:0x102A85028 = libmylib.dylib:__ZTI9MyFactory, *0x102A85028 = 0x102A52050
dyld: weak bind: libmylib_copy.dylib:0x102A85040 = libmylib.dylib:__ZTS8IFactory, *0x102A85040 = 0x102A51F41
dyld: weak bind: libmylib_copy.dylib:0x102A85058 = libmylib.dylib:__ZTS9MyFactory, *0x102A85058 = 0x102A51F36
dyld: weak bind: libmylib_copy.dylib:0x102A85010 = libmylib.dylib:__ZTV9MyFactory, *0x102A85010 = 0x102A52020
dyld: weak bind: libmylib_copy.dylib:0x102A85018 = libstdc++.6.dylib:__Znwm, *0x102A85018 = 0x7FFF938F0325
Handle C: 0x7fc729c03c20    Function C: 0x102a84ee0  Count: 3

Если вы посмотрите на weak bind линии вы увидите, что символы, которые мы видели ранее, были помечены как weak external разрешаются к символам с тем же именем в libmylib.dylib, Я считаю, что это поведение относится к правилу One Definition C ++ («Каждая программа должна содержать ровно одно определение каждой не встроенной функции или объекта, который используется в этой программе»). Поскольку в вашей программе имеется несколько определений одной и той же не встроенной функции, компоновщик пытается объединить символы во время загрузки, поэтому используется только одна функция.

Лучшее решение для каждой библиотеки — разместить свои символы в разных пространствах имен. Поскольку вы намереваетесь экспортировать только функцию фабрики C, анонимного пространства имен будет достаточно. Это также позволяет пометить оставшиеся символы как внешние, что может быть полезно для времени загрузки библиотеки.

Кроме того, вы можете немного обмануть правило единого определения, экспортируя только те символы, которые нужны клиентам вашей библиотеки:

$ clang++ -Wl,-exported_symbol -Wl,_GetFactory -dynamiclib myclass.cpp -o libmylib.dylib
$ cp libmylib.dylib libmylib_copy.dylib
$ ./mytest
Handle A: 0x7fc593403910    Function A: 0x1009e4e90  Count: 1
Handle B: 0x7fc593403910    Function B: 0x1009e4e90  Count: 2
Handle C: 0x7fc593403b10    Function C: 0x1009e7e90  Count: 1

Это вызывает weak external Символы, которые мы видели ранее, должны быть помечены как закрытые, и поэтому они будут преобразовываться только в символы в пределах одного изображения.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]