2016-11-24 7 views
1

Я заменяю operator new и operator delete внутри общей библиотеки. Я хочу, чтобы, когда я делал dlopen в этой общей библиотеке, были использованы замененные версии operator new и operator delete из общей библиотеки (не версии из двоичного файла, который запускает dlopen()). Для этого я передаю -Bsymbolic вариант для компоновщика, и кажется, что он не работает.linker option -Bsymbolic

Вот MWE:

main.cpp

#include <dlfcn.h> 

#include <iostream> 

#include "library.h" 

int main(int argc, const char* argv[]) 
{ 
    std::cout << "going call library function" << std::endl; 

    using func_t = decltype(library_function); 
    auto handle = dlopen("./libshared.so", RTLD_NOW); 
    auto fnc = reinterpret_cast<func_t*>(dlsym(handle, "library_function")); 
    fnc(); 
    dlclose(handle); 
} 

library.h

#ifndef LIBRARY_H 
#define LIBRARY_H 

extern "C" void library_function(); 

#endif // LIBRARY_H 

library.cpp:

#include <iostream> 
#include <string> 

extern "C" void library_function() 
{ 
    std::string str; 

    str.resize(10); 

    std::cout << "inside library function: " << str.size() << std::endl; 
} 

new.cpp

#include <iostream> 
#include <new> 

void* operator new(std::size_t size) 
{ 
    std::cout << "operator new from shared library" << std::endl; 

    void* ptr = malloc(size); 

    if(!ptr) throw std::bad_alloc(); 

    return ptr; 
} 

void* operator new(std::size_t size, const std::nothrow_t&) noexcept 
{ 
    std::cout << "operator new from shared library" << std::endl; 

    return malloc(size); 
} 

void* operator new[](std::size_t size) 
{ 
    return ::operator new(size); 
} 

void* operator new[](std::size_t size, const std::nothrow_t& nothrow) noexcept 
{ 
    return ::operator new(size, nothrow); 
} 

void operator delete(void* ptr) noexcept 
{ 
    std::cout << "operator delete from shared library" << std::endl; 

    return free(ptr); 
} 

void operator delete(void* ptr, std::size_t size) noexcept 
{ 
    ::operator delete(ptr); 
} 

void operator delete(void* ptr, const std::nothrow_t&) noexcept 
{ 
    return ::operator delete(ptr); 
} 

void operator delete(void* ptr, std::size_t size, const std::nothrow_t&) noexcept 
{ 
    return ::operator delete(ptr); 
} 

void operator delete[](void* ptr) noexcept 
{ 
    return ::operator delete(ptr); 
} 

void operator delete[](void* ptr, std::size_t size) noexcept 
{ 
    return ::operator delete(ptr); 
} 

void operator delete[](void* ptr, const std::nothrow_t&) noexcept 
{ 
    return ::operator delete(ptr); 
} 

void operator delete[](void* ptr, std::size_t size, const std::nothrow_t&) noexcept 
{ 
    return ::operator delete(ptr); 
} 

Makefile:

all: 
    c++ -std=c++14 -g2 -O0 -shared -fPIC -o libshared.so library.cpp new.cpp -Wl,-Bsymbolic 

    c++ -std=c++14 -g2 -O0 main.cpp -o main -ldl 

Выход из основной:

$ ./main 
going call library function 
inside library function: 10 

Ожидаемый результат: вывод содержит "оператор нового из разделяемой библиотеки", "оператор удаления из разделяемой библиотеки".

И второй вопрос - как такое же поведение может быть достигнуто при явной связи с этой библиотекой (-lshared для main.cpp).

Update:

кажется, что на самом деле линкер внести изменения W/или без -Bsymbolic. Учитывая diff из readelf -r на общих библиотеках ж/и з/о -Bsymbolic:

-Relocation section '.rela.plt' at offset 0xeb0 contains 20 entries: 
+Relocation section '.rela.plt' at offset 0xeb0 contains 15 entries: 
    Offset   Info   Type   Sym. Value Sym. Name + Addend 
-000000202018 002700000007 R_X86_64_JUMP_SLO 00000000000014b7 operator new(unsigned long, std::nothrow_t const&) + 0 
-000000202020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
-000000202028 003000000007 R_X86_64_JUMP_SLO 000000000000142c operator new(unsigned long) + 0 
-000000202030 000600000007 R_X86_64_JUMP_SLO 0000000000000000 std::ios_base::Init::[email protected]_3.4 + 0 
-000000202038 000700000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
-000000202040 003100000007 R_X86_64_JUMP_SLO 000000000000153f operator delete(void*) + 0 
-000000202048 000800000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
-000000202050 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4 + 0 
-000000202058 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
-000000202060 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
-000000202068 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
-000000202070 002e00000007 R_X86_64_JUMP_SLO 00000000000016b8 std::exception::exception() + 0 
-000000202078 001200000007 R_X86_64_JUMP_SLO 0000000000000000 std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned long)@GLIBCXX_3.4 + 0 
-000000202080 002f00000007 R_X86_64_JUMP_SLO 00000000000016d6 std::bad_alloc::bad_alloc() + 0 
-000000202088 001500000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.4 + 0 
-000000202090 001600000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_1.3 + 0 
-000000202098 001700000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
-0000002020a0 001800000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_1.3 + 0 
-0000002020a8 001900000007 R_X86_64_JUMP_SLO 0000000000000000 std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))@GLIBCXX_3.4 + 0 
-0000002020b0 001c00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.0 + 0 
+000000202018 000300000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
+000000202020 000600000007 R_X86_64_JUMP_SLO 0000000000000000 std::ios_base::Init::[email protected]_3.4 + 0 
+000000202028 000700000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
+000000202030 000800000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
+000000202038 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4 + 0 
+000000202040 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
+000000202048 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0 
+000000202050 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
+000000202058 001200000007 R_X86_64_JUMP_SLO 0000000000000000 std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned long)@GLIBCXX_3.4 + 0 
+000000202060 001500000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.4 + 0 
+000000202068 001600000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_1.3 + 0 
+000000202070 001700000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.4.21 + 0 
+000000202078 001800000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_1.3 + 0 
+000000202080 001900000007 R_X86_64_JUMP_SLO 0000000000000000 std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))@GLIBCXX_3.4 + 0 
+000000202088 001c00000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_3.0 + 0 

кажется, что оператор новый/оператор удаления не помечается, перемещаемым при строительстве с -Bsymbolic - поэтому внутренняя версия должна быть использована?

Update: Действительно, я вижу разницу в вызове оператора нового:

без -Bsymbolic вызов проходит через PLT и GOT:

0x000000000000109c <+28>: callq 0xed0 <[email protected]> 

ж/-Bsymbolic вызов не идет через PLT и GOT:

0x0000000000000f7c <+28>: callq 0x110a <operator new[](unsigned long)> 

Обновление:

На самом деле кажется, что правильная версия оператора new/operator delete называется. Эта инструкция в GDB показывает это. Проблема в том, что оператор < < в поток cout из общей библиотеки не работает.

Update:

Кажется, что проблема вызвана тем, что, когда я звоню на размер станд :: строка - символ используется из бинарного, а не из библиотеки. Внутри функции std :: string resize от двоичного вызова оператор new перенаправляется на локальное определение - поэтому я не вижу вызова замененного оператора new/operator delete. Он начинает работать, когда я напрямую использую оператор new/operator delete из библиотеки.

Update:

Да, проблема исчезает, когда статически связывая с libstdc++ (libc++).

+0

Итак, у вас есть программа на C++, которая использовала один набор функций 'operator new' и' operator delete' (потому что он был запущен до того, как вы получили вызов 'dlopen()'). Затем вы хотите загрузить свою общую библиотеку через 'dlopen()' и использовать ее версии управления памятью для всей существующей памяти и любой будущей памяти. Как быстро вы можете сказать «сбой!»? –

+0

@JonathanLeffler, предположим, что я полностью понимаю проблему, и я знаю, что делаю. Фактический ответ более сложный - все оператор new/operator delete из общей библиотеки будет перенаправлен на оператор new/operator delete из двоичного файла, который сделал dlopen, но через специальный интерфейс (callgate) между двоичной и общей библиотекой. – user1641854

+0

OK; на вашей собственной голове. Но я озадачен тем, что вам нужно задать этот вопрос, если вы соответствуете этим требованиям. Я понятия не имею, и не рискнул бы, но это только я. –

ответ

1

Приведенный пример фактически работает. Проблема заключается в том, что сценарий проверки для проблемы (выделение памяти методом вызова std :: string resize) является плохим. Поскольку метод вызова для изменения размера динамически маршрутизируется PLT/GOT в версию из двоичного файла. И метод std :: string resize в двоичных вызовах имеет свой собственный operator new/operator delete (не версии из разделяемой библиотеки). Чтобы обойти это - мы можем статически компилировать с помощью libstdC++ (-static-libstdc++).