Вопрос по c++ – Почему мой новый оператор не называется

8

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

<code>#include <exception>
#include <new>
#include <cstdlib>
#include <cstdio>
#include "base.hpp"
void* operator new(size_t size) {
    std::printf("New of library called\n");
    void *p=std::malloc(size); 
    if (p == 0) // did malloc succeed?
        throw std::bad_alloc(); // ANSI/ISO compliant behavior
    return p;
}
void operator delete(void* p) {
    std::printf("Delete of library called\n");
    std::free(p);
}
class Derived : public Base {
public:
    Derived() : Base(10) { }
};
extern "C" {
    Base* create() {
        return new Derived;
    }
    void destroy(Base* p) {
        delete p;
    }
}
</code>

и скомпилировал это с

<code>g++ -g -Wall -fPIC -shared library.cpp -o library.so
</code>

или как занятый русский предложил попробовать (но в итоге ничего не изменилось)

<code>g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so
</code>

Класс Base содержит только значение типа int и функцию get_value () для получения этого значения. После этого я написал client.cpp вот так

<code>#include <exception>
#include <new>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <dlfcn.h>
#include "base.hpp"
void* operator new(size_t size) {
    std::printf("New of client called\n");
    void *p=std::malloc(size); 
    if (p == 0) // did malloc succeed?
        throw std::bad_alloc(); // ANSI/ISO compliant behavior
    return p;
}
void operator delete(void* p) {
    std::printf("Delete of client called\n");
    std::free(p);
}
typedef Base* create_module_t();
typedef void destroy_module_t(Base *);

int main() {
    void* handle = dlopen("./library.so", 
        RTLD_LAZY);
    if (handle == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    }
    create_module_t* create_module = NULL;
    void* func = dlsym(handle, "create");
    if (func == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    } else create_module = (create_module_t *)func;
    destroy_module_t* destroy_module = NULL;
    func = dlsym(handle, "destroy");
    if (func == NULL) {
        std::cout << dlerror() << std::endl;
        return 1;
    } else destroy_module = (destroy_module_t *)func;
    Base* a = create_module();
    std::cout << "Value: " << a->get_value() << std::endl;
    destroy_module(a);
    return 0;
}
</code>

и скомпилировал это с

<code>g++ -Wall -g -o client -ldl client.cpp
</code>

При выполнении клиента я получаю только «Новый клиент под названием» и "Удалить клиента с именем". Даже если я использую переключатель компилятора -Bsymbolic для библиотеки, как предложил Employed Russian.

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

Дополнительный вопрос: зачем мне функция destroy (Base * p)? Если бы эта функция вызывала только оператор удаления клиента, я мог бы также сделать это сам, т.е. вместо destroy_module (a) рядом с последней строкой.

Ответ, который я нашел: Библиотека также может предоставить новую пару / оператор удаления. Так что, если я сначала использую новую библиотеку, а затем удаляю клиента, я, вероятно, попаду в ловушку. К сожалению, до сих пор я никогда не видел свою библиотеку, использующую ее новую или удаленную ... Так что на оригинальный вопрос до сих пор нет ответа.

Дополнение: я имею в виду только платформу Linux.

Изменить: Важные части находятся в комментариях к Ответу занятого русского языка. Итак, я даю основную подсказку в двух словах: если так назвать gcc

<code>g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
</code>

библиотека будет использовать собственные операторы new / delete. В противном случае результаты

<code>g++ -Wall -g -fPIC -shared library.cpp -o library.so
</code>

в библиотеке, которая использует операторы new / delete вызывающей программы. Спасибо занятым русским!

Я заявил, что «использовал его в своей тестовой программе» немного точнее в моем посте. Но я все еще не знаю, что вы имеете в виду под LD_PRELOAD. phlipsy
Хорошо, я отредактировал свой вопрос и добавил еще немного информации. Надеюсь, теперь это стало более понятным. phlipsy
Я думаю, вам нужно обновить исходный вопрос, чтобы избежать путаницы, которую я вижу ниже. Дайте понять, что new / delete живут в вашем .dll, и покажите реализацию вашего & quot; create & quot; функция (которую я воображаю, просто называет «новой»). Кроме того, на какой платформе вы работаете? Если это то, что я подозреваю, printf не сработает. Jim Buck
Почему это грустно? Разве это не доказывает, что он использует свои собственные новые и удаления? Это то, что вы тестировали, не так ли? 0xC0DEFACE
Как вы "использовали его в моей тестовой программе"? ? Вы пробовали LD_PRELOAD? J-16 SDiZ

Ваш Ответ

4   ответа
11

Проблема в том, что на большинствеUNIX платформы (в отличие отWin32 а такжеAIX) все ссылки на символы по умолчанию связаны сfirst определение символа, видимого для загрузчика во время выполнения.

Если вы определите'operator new' в основномa.outвсе будет привязано к этому определению (как показывает пример Нейла Баттерворта), потому чтоa.out это самый первый поиск загрузчика во время выполнения изображения.

Если вы определите его в библиотеке, которая загружается послеlibC.so (или жеlibstdc++.so если вы используетеGCC), тогда ваше определение никогда не будет использовано. Так как выdlopen()после запуска программы,libC к этому моменту уже загружен, и ваша библиотека является самой последней, которую будет искать загрузчик времени выполнения; так что ты проиграл.

НаELF платформы, вы можете изменить поведение по умолчанию, используя-Bsymbolic, Отman ld в Linux:

 -Bsymbolic
   When creating a shared library, bind references to global symbols
   to the definition within the shared library, if any. Normally, it
   is possible for a program linked against a shared library to override
   the  definition within the shared library. This option is only meaningful
   on ELF platforms which support shared libraries.

Обратите внимание, что-Bsymbolic это флаг компоновщика, а не флаг компилятора. При использованииg++Вы должны передать флаг компоновщику следующим образом:

  g++ -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
Вы никогда не раскрывали, какая у вас ОС. Вот что я вижу в Linux: $ g ++ -g -Wall -fPIC -shared library.cpp -o library.so $ g ++ -g client.cpp -ldl $ ./a.out Новый клиент с именем Value: 10 Удаление из клиент с именем $ g ++ -g -Wall -fPIC -shared library.cpp -o library.so -Wl, -Bsymbolic $ ./a.out Новое библиотеки с именем Value: 10 Удаление библиотеки с именем
Но так ли это, если я явно перегрузил оператор new / delete в моей библиотеке? Я отредактировал свой исходный вопрос, чтобы проблема стала более понятной. phlipsy
Вот и все, теперь я тоже это вижу! И да, я тоже использую Linux. Спасибо за терпеливость. phlipsy
Итак, я назвал & quot; g ++ -g -Wall -fPIC -shared -Wl, -Bsymbolic library.cpp -o library.so & quot; и клиент, как я написал это в моем вопросе. Но с другой стороны, только новый / удаленный клиент был вызван. Я загружал библиотеку динамически, клиент вообще не связан с ней. Является ли линкерный переключатель "-Bsymbolic" также предназначен для этой ситуации? phlipsy
Да, это все еще так. Что касается загрузчика времени выполнения, вы только что определили два экземпляра глобальной функции с именем _Znwj: один в основном исполняемом файле, другой в library.so. Первым, кто увидит загрузчик среды выполнения, будет тот, который он будет использовать. Как далеко, почему -Bsymbolic не работал; скорее всего, вы допустили ошибку: вы делали & gas; -g -Bsymbolic -Wall -fPIC -shared library.cpp -o library.so & apos ;? «-Символический» является переключателем компоновщика, чтобы передать его компоновщику, используйте «-Wl, -Bsymbolic»; в противном случае это может быть интерпретировано GCC (что имеет совершенно другое значение для -B ... flags).
1

Следующий код работает как положено. Ожидаете ли вы, что код вашей динамической библиотеки будет использовать предоставленную вами новую / удаленную версию? Я думаю, вы будете разочарованы.

#include <memory>
#include <cstdio>
#include <cstdlib>
using namespace std;;

void* operator new(size_t size) {
        std::printf("New...\n");
        void *p=std::malloc(size); 
        if (p == 0) // did malloc succeed?
                throw std::bad_alloc(); // ANSI/ISO compliant behavior
        return p;
}

void operator delete(void* p) {
        std::printf("Delete...\n");
        std::free(p);
}

int main() {
    int * p = new int(42);
    delete p;
}
Нет, я не предоставил новый новый / удалить в "клиенте" программа для библиотеки. Я определилin моя библиотека отправила новый файл new / delete, и я хотел убедиться, что библиотека использует его самостоятельно, то есть этот новый new / delete. phlipsy
@ph, как вы можете ожидать, что new будет вызываться, если вы им не пользуетесь? Я не вижу этого, я, должно быть, плохо встал сегодня утром.
@litb: мысль, что библиотека использует свои собственные новые / delete-операторы, чем те, которые определены в клиентской программе. Я определил в своей библиотеке и в моей клиентской программе различные новые операторы new / delete. Но называются только новые / delete-операторы в клиентской программе. Это предназначено? phlipsy
1

Посмотрите в RTLD_DEEPBIND

0

Я думаю, проблема в том, что перегрузка операторов в C ++ - это функция времени компиляции, а не ссылки. DLL была скомпилирована без знания перегруженной функции new (), поэтому она не будет работать правильно.

Другая возможность состоит в том, что на вашей платформе он будет работать с использованием ссылок (как в вашем примере), но DLL не разрешает символы из вашего исполняемого файла.

Возможно, я сформулировал свой вопрос немного неясным: я определил в своей библиотеке новый оператор new / delete и был удивлен, что библиотека так или иначе использует оператор new / delete клиента. Смотрите также мой отредактированный вопрос. phlipsy

Похожие вопросы