Как вызывать методы объекта, который создается путем загрузки времени выполнения общего объекта с использованием dlopen - PullRequest
0 голосов
/ 28 декабря 2018

Я создал тестовую библиотеку libmathClass.so, которую я буду загружать из кода ниже.Этот общий объект имеет класс, и создается библиотечный вызов для возврата объекта этого класса.Как я могу вызвать методы этого объекта из основного кода, показанного ниже.Я получаю неопределенную ошибку ссылки от ld (linker), так как он не знает об определении методов.

void* handle;
handle=dlopen("math1/libmathClass.so", RTLD_LAZY);
if(!handle)
{
    cout<<"error loading library: "<<dlerror()<<endl;
    exit(2);
}
else
{
    cout<<"***libmathClass.so library load successful!"<<endl;
}

void* (*mathInit) ();
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");
if(!mathInit)
{
    cout<<"error loading instance method: "<<dlerror()<<endl;
    exit(3);
}
else
{
    cout<<"***method load successful!"<<endl;
}

mathOperationClass *mathInstance;
auto obj = (*mathInit)();
if(!obj)
{
    cout<<"object is not created"<<endl;
    exit(4);
}
else
{
    cout<<"object created!!!"<<endl;
    mathInstance = reinterpret_cast<mathOperationClass *>(obj);
}


int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
cout<< mathInstance->AddInt(num1, num2)<<endl;

Команда, которую я использовал для компиляции - g ++ --std = c ++ 11 -g -o dynamicTestdynamicMain.cpp -ldl

Сообщение об ошибке: dynamicMain.cpp: 54: неопределенная ссылка на `mathOperationClass :: AddInt (int, int) 'collect2: ошибка: ld вернул 1 состояние выхода

1 Ответ

0 голосов
/ 28 декабря 2018
mathInit = (void* (*)())dlsym(handle, "CreateMathOperationInstance");

Здесь вы используете dlsym(), чтобы найти этот символ в общей библиотеке.Это должна быть функция со связью C, поскольку имя символа не искажено.Это важно, и имейте это в виду, когда вы смотрите на эту строку:

cout<< mathInstance->AddInt(num1, num2)<<endl;

Здесь AddInt - это метод класса, на который указывает mathInstance.Метод класса - это просто другая функция, за исключением того, что он всегда принимает скрытый указатель this в качестве дополнительного аргумента.Это то, чем является метод класса, во многих словах, и это фактически имеет место с типичной реализацией C ++.C ++ технически фактически не требует, чтобы это имело место.Реализация C ++ может свободно реализовывать методы любым способом, который дает результаты, соответствующие спецификации C ++.Но, на практике, в типичной реализации C ++ это и есть метод класса.Функция с дополнительным параметром, на которую ссылаются как this.

. Таким образом, приведенная выше строка в основном эквивалентна:

cout<< mathOperationClass::AddInt(mathInstance, num1, num2)<<endl;

Это в основном то, что происходитздесь, говоря очень свободно.

Этот метод / функция mathOperationClass::AddInt, предположительно, находится в той же общей библиотеке, которую вы dlopen -ed;и потому что вы dlopen -эдировали его и на самом деле не ссылались на него, у вас есть ссылка на этот символ, и эта ссылка не может быть разрешена во время выполнения, следовательно, ваша неопределенная ошибка символа во время выполнения.

Единственное единственноеВы даже можете надеяться на то, что вызовите этот метод класса - если он вообще может быть вызван таким способом - также с помощью dlsym().Но для того, чтобы хоть малейшая молитва о том, чтобы на самом деле это удалось, нужно совершить целую кучу вещей.

Во-первых, вы должны выяснить фактический искаженный C ++.имя символа .Используя мой компилятор Linux x86_64 g ++ в качестве ссылки, искаженное имя для этого метода будет «_ZN18mathOperationClass6AddIntEii».Имея это в виду, вы можете использовать dlsym, чтобы найти этот символ (или каково бы ни было действительное искаженное имя символа вашей реализации C ++ для этого метода) в вашей общей библиотеке.

И когда у вас есть этот символ, что теперь?Что ж, будем надеяться, что у вашей реализации C ++ действительно есть взломанный ABI C ++, где вы можете вызвать метод класса, явно передав ему дополнительный параметр this, что-то вроде этого:

int (*addInt)(mathOperationClass *, int, int)=
    reinterpret_cast<int (*)(mathOperationClass *, int, int)>
        (dlsym(handle, "_ZN18mathOperationClass6AddIntEii"));

cout << (*addInt)(mathInstance, num1, num2) << endl;

Весь этоткарточный домик рухнет, если не будет подтверждено, что методы C ++ могут быть вызваны таким хакерским способом, в ABI вашей реализации C ++.Поскольку вы уже используете dlopen (), вы уже находитесь в непереносимой территории, используя ресурсы, специфичные для реализации C ++, поэтому вы можете также выяснить, могут ли ваши методы C ++ вызываться таким образом.Если нет, вам придется выяснить, как они могут быть вызваны, используя простой указатель.

А теперь что-то совершенно другое ...

Сказав все вышеперечисленное:

Есть один способ избежать этого беспорядка: сделать этот метод класса методом виртуального класса.Методы виртуального класса отправляются через внутреннюю таблицу виртуальных функций.Поэтому попробуйте объявить этот метод AddInt как метод виртуального класса и вызвать его как есть.Скорее всего, это будет работать в вашей реализации C ++, так как компилятор не будет выдавать, в данном случае, явную ссылку на символ для mathOperationClass::AddInt.Он найдет метод с помощью таблицы виртуальных функций, которая незаметно присоединяется к каждому экземпляру объекта.

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

...