Получить адрес void* нестатической функции-члена для dladdr
Нельзя привести нестатическую функцию-член к void*. Используйте dlsym() с манглированным именем или обёртку, чтобы передать dladdr() и определить библиотеку.
Как получить адрес void* для нестатической member‑функции, чтобы передать его в dladdr и выяснить, из какой shared‑library загружена функция?
Для статических функций я могу просто написать:
void* fAddr = reinterpret_cast<void*>( &A::f );
но для нестатической member‑функции, например void g() в struct A, приведение запрещено:
void* gAddr = reinterpret_cast<void*>( &A::g ); // ❌ не разрешено
Какой код должен заменить gAddr = ???( &A::g );, чтобы получить указатель, который dladdr сможет принять?
#include <dlfcn.h>
#include <iostream>
struct __attribute__((visibility("default"))) A {
static void f() {}
void g() {}
};
void* fAddr = reinterpret_cast<void*>( &A::f );
// void* gAddr = ???( &A::g );
int main() {
Dl_info info;
dladdr(fAddr, &info);
std::cout << info.dli_fname; // выводит имя shared library
// то же самое для gAddr
}
Я попробовал reinterpret_cast<void*>( &A::g ) в Clang и получил:
error: reinterpret_cast from 'void (A::*)()' to 'void *' is not allowed
Что нужно использовать вместо ???, чтобы получить пригодный для dladdr адрес?
Адрес, который ожидает dladdr(), — это адрес обычной функции (или объекта данных).
Нестатический член‑функция не является обычной функцией – её тип void (A::*)(). На самом деле, в большинстве ABI представление указателя на член содержит не просто один машинный адрес (может включать поправку для виртуальных функций, множественного наследования и т.д.), и стандарт C++ явно запрещает преобразовывать его в void*.
Вы можете задаться вопросом, почему так, но правило очевидно.
Таким образом, нет переносимого или стандарта‑совместимого способа получить void* из нестатического члена‑функции, который можно передать в dladdr(). Единственные корректные подходы:
| Что вы хотите | Как это сделать |
|---|---|
| Получить файл/библиотеку, содержащую реализацию члена | Использовать манглённое имя символа с dlsym() (или проверкой link_map) вместо dladdr(). |
Вызвать dladdr() на функции, которая вызывает член |
Написать небольшую статическую или свободную обёртку, которая перенаправляет вызов, и передать её адрес в dladdr(). |
Ниже два распространённых шаблона.
1. Используйте dlsym() с манглённым именем
Компиляторы Linux манглют имена член‑функций предсказуемым способом:
#include <dlfcn.h>
#include <iostream>
#include <cxxabi.h>
#include <string>
struct A {
void g() {}
};
int main() {
// Get the mangled name of A::g
std::string mangled = abi::__cxa_demangle("vN1A1gEv", nullptr, nullptr, nullptr);
// In practice you would know the mangled name or obtain it with
// `nm`, `objdump`, or a compiler‑specific API.
void *sym = dlsym(RTLD_DEFAULT, mangled.c_str());
if (!sym) {
std::cerr << "symbol not found\n";
return 1;
}
Dl_info info;
dladdr(sym, &info);
std::cout << "function is in " << info.dli_fname << '\n';
}
Таким образом, вы полностью обойдёте преобразование в void*.
2. Вызовите dladdr() на обёртке
Если хотите оставить саму член‑функцию в выводе, оберните её в обычную функцию, которую можно передать в dladdr():
#include <dlfcn.h>
#include <iostream>
struct A {
void g() { std::cout << "A::g\n"; }
};
// Wrapper that forwards the call
extern "C" void wrapper_A_g(A *self) {
self->g(); // call the member
}
int main() {
// Get the address of the wrapper
void *wrapperAddr = reinterpret_cast<void*>(&wrapper_A_g);
Dl_info info;
dladdr(wrapperAddr, &info);
std::cout << "wrapper is in " << info.dli_fname << '\n';
// You can still use the original member function normally
A a;
(a.*&A::g)(); // call the member directly
}
wrapper_A_g — обычная C‑функция, поэтому её адрес можно безопасно преобразовать в void* и обработать в dladdr(). Обёртка не даёт точный адрес реализации член‑функции, но обеспечивает стабильную точку входа, принадлежащую тому же разделяемому объекту.
Итоги
- Преобразование указателя на нестатический член‑функцию в
void*является неопределённым поведением и не допускается стандартом C++. dladdr()нельзя использовать напрямую с адресом нестатического члена‑функции.- Если нужна информация о библиотеке, используйте разрешение символов (
dlsym()) или обёртку.
Это единственные переносимые, стандарта‑совместимые способы добиться желаемого.