链接器选项 -Bsymbolic
linker option -Bsymbolic
我在共享库中替换了 operator new
和 operator delete
。
我希望当我在这个共享库上执行 dlopen
时,使用共享库中替换的 operator new
和 operator delete
版本(而不是运行 dlopen()
的二进制版本)。
为了实现这一点,我将 -Bsymbolic 选项传递给 linker,它似乎不起作用。
这是 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);
}
生成文件:
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
预期输出:输出包含 "operator new from shared library" , "operator delete from shared library" .
第二个问题 - 当我显式 link 使用此库(-lshared for main.cpp)时如何实现相同的行为。
更新:
似乎实际上 linker 进行了更改 w/ 或 w/o -Bsymbolic
。
考虑共享库上 readelf -r
的 diff
和 w/o -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 _ZNSt7__cxx1112basic_s@GLIBCXX_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::Init@GLIBCXX_3.4 + 0
-000000202038 000700000007 R_X86_64_JUMP_SLO 0000000000000000 malloc@GLIBC_2.2.5 + 0
-000000202040 003100000007 R_X86_64_JUMP_SLO 000000000000153f operator delete(void*) + 0
-000000202048 000800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_atexit@GLIBC_2.2.5 + 0
-000000202050 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZStlsISt11char_traits@GLIBCXX_3.4 + 0
-000000202058 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
-000000202060 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 free@GLIBC_2.2.5 + 0
-000000202068 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_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 __stack_chk_fail@GLIBC_2.4 + 0
-000000202090 001600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_allocate_excepti@CXXABI_1.3 + 0
-000000202098 001700000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNKSt7__cxx1112basic_@GLIBCXX_3.4.21 + 0
-0000002020a0 001800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_throw@CXXABI_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 _Unwind_Resume@GCC_3.0 + 0
+000000202018 000300000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
+000000202020 000600000007 R_X86_64_JUMP_SLO 0000000000000000 std::ios_base::Init::Init@GLIBCXX_3.4 + 0
+000000202028 000700000007 R_X86_64_JUMP_SLO 0000000000000000 malloc@GLIBC_2.2.5 + 0
+000000202030 000800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_atexit@GLIBC_2.2.5 + 0
+000000202038 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZStlsISt11char_traits@GLIBCXX_3.4 + 0
+000000202040 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
+000000202048 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 free@GLIBC_2.2.5 + 0
+000000202050 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_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 __stack_chk_fail@GLIBC_2.4 + 0
+000000202068 001600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_allocate_excepti@CXXABI_1.3 + 0
+000000202070 001700000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNKSt7__cxx1112basic_@GLIBCXX_3.4.21 + 0
+000000202078 001800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_throw@CXXABI_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 _Unwind_Resume@GCC_3.0 + 0
似乎 operator new/operator delete 在使用 -Bsymbolic 构建时未标记为可重定位 - 因此必须使用内部版本?
更新:
事实上,我看到调用 operator new 的不同之处:
w/o -B符号调用通过PLT和GOT:
0x000000000000109c <+28>: callq 0xed0 <_Znam@plt>
w/ -B 符号调用不通过 PLT 和 GOT:
0x0000000000000f7c <+28>: callq 0x110a <operator new[](unsigned long)>
更新:
实际上似乎调用了 operator new/operator delete 的正确版本。 GDB 中的步骤说明显示了它。问题是 operator<< 从共享库 cout stream 不工作。
更新:
问题似乎是由以下事实引起的:当我在 std::string 上调用调整大小时 - 该符号是从二进制文件中使用的,而不是从库中使用的。在二进制文件的 std::string resize 函数中,对 operator new 的调用被路由到其本地定义 - 这就是为什么我没有看到替换的 operator new/operator delete 的调用。
当我直接使用 operator new/operator delete from library.
时它开始工作
更新:
是的,使用 libstdc++
(libc++
).
静态 linking 时问题消失
提供的示例确实有效。
问题是问题的验证场景(通过调用 std::string resize 方法分配内存)是错误的。因为对 resize 方法的调用由 PLT/GOT 动态路由到二进制版本。并且 std::string resize 二进制调用它自己的 operator new
/operator delete
方法(不是来自共享库的版本)。
为了解决这个问题——我们可以使用 libstdc++(-static-libstdc++
).
进行静态编译
我在共享库中替换了 operator new
和 operator delete
。
我希望当我在这个共享库上执行 dlopen
时,使用共享库中替换的 operator new
和 operator delete
版本(而不是运行 dlopen()
的二进制版本)。
为了实现这一点,我将 -Bsymbolic 选项传递给 linker,它似乎不起作用。
这是 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);
}
生成文件:
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
预期输出:输出包含 "operator new from shared library" , "operator delete from shared library" .
第二个问题 - 当我显式 link 使用此库(-lshared for main.cpp)时如何实现相同的行为。
更新:
似乎实际上 linker 进行了更改 w/ 或 w/o -Bsymbolic
。
考虑共享库上 readelf -r
的 diff
和 w/o -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 _ZNSt7__cxx1112basic_s@GLIBCXX_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::Init@GLIBCXX_3.4 + 0
-000000202038 000700000007 R_X86_64_JUMP_SLO 0000000000000000 malloc@GLIBC_2.2.5 + 0
-000000202040 003100000007 R_X86_64_JUMP_SLO 000000000000153f operator delete(void*) + 0
-000000202048 000800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_atexit@GLIBC_2.2.5 + 0
-000000202050 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZStlsISt11char_traits@GLIBCXX_3.4 + 0
-000000202058 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
-000000202060 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 free@GLIBC_2.2.5 + 0
-000000202068 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_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 __stack_chk_fail@GLIBC_2.4 + 0
-000000202090 001600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_allocate_excepti@CXXABI_1.3 + 0
-000000202098 001700000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNKSt7__cxx1112basic_@GLIBCXX_3.4.21 + 0
-0000002020a0 001800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_throw@CXXABI_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 _Unwind_Resume@GCC_3.0 + 0
+000000202018 000300000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
+000000202020 000600000007 R_X86_64_JUMP_SLO 0000000000000000 std::ios_base::Init::Init@GLIBCXX_3.4 + 0
+000000202028 000700000007 R_X86_64_JUMP_SLO 0000000000000000 malloc@GLIBC_2.2.5 + 0
+000000202030 000800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_atexit@GLIBC_2.2.5 + 0
+000000202038 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZStlsISt11char_traits@GLIBCXX_3.4 + 0
+000000202040 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_3.4.21 + 0
+000000202048 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 free@GLIBC_2.2.5 + 0
+000000202050 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNSt7__cxx1112basic_s@GLIBCXX_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 __stack_chk_fail@GLIBC_2.4 + 0
+000000202068 001600000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_allocate_excepti@CXXABI_1.3 + 0
+000000202070 001700000007 R_X86_64_JUMP_SLO 0000000000000000 _ZNKSt7__cxx1112basic_@GLIBCXX_3.4.21 + 0
+000000202078 001800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_throw@CXXABI_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 _Unwind_Resume@GCC_3.0 + 0
似乎 operator new/operator delete 在使用 -Bsymbolic 构建时未标记为可重定位 - 因此必须使用内部版本?
更新: 事实上,我看到调用 operator new 的不同之处:
w/o -B符号调用通过PLT和GOT:
0x000000000000109c <+28>: callq 0xed0 <_Znam@plt>
w/ -B 符号调用不通过 PLT 和 GOT:
0x0000000000000f7c <+28>: callq 0x110a <operator new[](unsigned long)>
更新:
实际上似乎调用了 operator new/operator delete 的正确版本。 GDB 中的步骤说明显示了它。问题是 operator<< 从共享库 cout stream 不工作。
更新:
问题似乎是由以下事实引起的:当我在 std::string 上调用调整大小时 - 该符号是从二进制文件中使用的,而不是从库中使用的。在二进制文件的 std::string resize 函数中,对 operator new 的调用被路由到其本地定义 - 这就是为什么我没有看到替换的 operator new/operator delete 的调用。 当我直接使用 operator new/operator delete from library.
时它开始工作更新:
是的,使用 libstdc++
(libc++
).
提供的示例确实有效。
问题是问题的验证场景(通过调用 std::string resize 方法分配内存)是错误的。因为对 resize 方法的调用由 PLT/GOT 动态路由到二进制版本。并且 std::string resize 二进制调用它自己的 operator new
/operator delete
方法(不是来自共享库的版本)。
为了解决这个问题——我们可以使用 libstdc++(-static-libstdc++
).