Linux с ++. Вызвать функцию вызова базового класса, определенного внутри предварительно загруженной разделяемой библиотеки - PullRequest
0 голосов
/ 14 сентября 2018

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

base.h

class Base
{
public:

    virtual ~Base () {}
    virtual void Function ();
};

base.cpp

#include <stdio.h>
#include "base.h"

void Base::Function ()
{
    printf ("base function\n");
}

mybase.so

g++ -fpic -g -shared base.cpp -o libbase.so

main.cpp

#include "base.h"

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    Derived* d = new Derived ();

    d->Function ();

    delete d;

    return 1;
}

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

test

g++ -fpic -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

Наконец, я использую переменную среды LD_PRELOAD для предварительной загрузки общей библиотеки перед выполнением

LD_PRELOAD=./libbase.so ./test
Segmentation fault (core dumped)

Я заметил, что проблема в том, что виртуальная таблица для производного объектане определено для "Function":

(gdb) info vtbl d
vtable for 'Derived' @ 0x601030 (subobject @ 0x602010):
[0]: 0x400c7e <Derived::~Derived()>
[1]: 0x400cc0 <Derived::~Derived()>
[2]: 0x0

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

Итакмой вопрос: есть ли способ сделать эту работу?Возможно, заставляя каким-либо образом загрузить разделяемую библиотеку перед исполняемым файлом ...

Кстати: если сделать функцию "Function" не виртуальной, все будет работать нормально, поскольку для производного класса vtable не требуется.

UPDATE 1: Использование объекта вместо указателя заставляет main работать:

int main (int argc, char** argv)
{
    Derived d;

    d.Function ();  // prints "base function"

    return 1;
}

ОБНОВЛЕНИЕ 2: Работает так же, как в основной, но во второй общей библиотеке:

mylib.cpp

#include "base.h"

class DerivedLib : public Base
{
};

extern "C" void do_function()
{
    DerivedLib* d = new DerivedLib();

    d->Function(); 

    delete d;
}

mylib.so

g++ -fPIC -g -shared lib.cpp -o libmylib.so 

main.cpp

#include "base.h"
#include <dlfcn.h>

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    void* handle = dlopen("libmylib.so", RTLD_LAZY);

    void (*do_function)();

    do_function = (void (*)())dlsym(handle, "do_function");

    do_function();  // prints "base function"

    Derived* d = new Derived();

    d->Function (); // <- crashes

    delete d;

    return 1;
}

Так что определенно проблема возникает, когда внутри исполняемого файла создается указатель нового экземпляра

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Решением (при наличии) является создание исполняемого файла PIE .

тест:

g++ -fpic -pie -fpie -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

LD_PRELOAD=./libbase.so ./test
base function
0 голосов
/ 16 сентября 2018

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

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

  2. Вам не нужно перекомпилировать исполняемый файл, если вы добавляете класс в общую библиотеку.Это просто не должно быть проблемой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...