Я использую функцию bfd_find_nearest_line
найти исходное местоположение функции (из исполняемого файла с символами отладки — скомпилировано с -g
). Естественно, один из аргументов — это указатель на функцию, которую я хочу найти:
boolean
_bfd_elf_find_nearest_line (abfd,
section,
symbols,
offset,
filename_ptr,
functionname_ptr, // <- HERE!
line_ptr)
https://sourceware.org/ml/binutils/2000-08/msg00248.html
После небольшого количества (чистого C) котельной пластины мне удалось это работать с обычными функциями (где указатель обычной функции приведен к *void
).
Например, это работает:
int my_function(){return 5;}
int main(){
_bfd_elf_find_nearest_line (...,
(void*)(&my_function),
...);
}
Вопрос в том, bfd_find_nearest_line
может использоваться для поиска исходного кода функции-члена класса.
struct A{
int my_member_function(){return 5.;}
};
_bfd_elf_find_nearest_line (...,
what_should_I_put_here??,
...)
Функция-член класса (в этом случае, если тип int (A::*)()
) не являются функциями, в частности, не может быть приведен ни к одному указателю функции, даже к void*
, Посмотреть здесь: https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr
Я полностью понимаю логику, стоящую за этим: когда-либо указатель на функцию-член является единственным дескриптором, из которого у меня есть информация о функции-члене, чтобы заставить BFD идентифицировать функцию. Я не хочу, чтобы этот указатель вызывал функцию.
Я более или менее знаю, как работает C ++, компилятор автоматически генерирует эквивалентную функцию free-C,
__A_my_member_function(A* this){...}
Но я не знаю, как получить доступ к адресу этой бесплатной функции или, если это вообще возможно, и является ли bfd
библиотека сможет найти исходное местоположение оригинала my_member_function
через этот указатель.
(По крайней мере, на данный момент меня не интересуют виртуальные функции.)
Другими словами,
1) мне нужно знать, если bfd
сможет найти функцию-член,
2) и если это возможно, как я могу отобразить указатель на функцию-член типа int (A::*)()
на аргумент, который bfd
могу взять (void*
).
Я знаю другими средствами (трассировка стека), что указатель существует, например, я могу получить, что в этом случае вызывается свободная функция _ZN1A18my_member_functionEv
, но проблема в том, как я могу получить это от &(A::my_member_function)
,
Хорошо, есть хорошие новости и плохие новости.
Хорошая новость: это является возможный.
Плохая новость: это не так просто.
Вам понадобится c++filt
полезность.
И какой-то способ чтения таблицы символов вашего исполняемого файла, например readelf
, Если вы можете перечислить [искаженные] символы с bfd_*
позвоните, вы можете быть в состоянии сохранить себе шаг.
Кроме того, вот Biggie: Вам понадобится c ++ имя вашего символа в текстовая строка. Таким образом, для &(A::my_member_function)
вам понадобится в форме: "A::my_member_function()"
Это не должно быть слишком сложно, так как я предполагаю, что у вас есть ограниченное количество таких, о которых вы заботитесь.
Вам нужно получить список символов и их адреса от readelf -s <executable>
, Будьте готовы разобрать этот вывод. Вам нужно будет декодировать шестнадцатеричный адрес из строки, чтобы получить ее двоичное значение.
Это будут искаженные имена. Для каждого символа сделайте c++filt -n mangled_name
и захватывать вывод (то есть трубу) во что-то (например, nice_name
). Это вернет вам деформированное имя (то есть красивое имя C ++, которое вы хотели бы).
Сейчас если nice_name
соответствует «A: my_member_function ()», теперь у вас есть совпадение, у вас уже есть искаженное имя, но, что более важно, шестнадцатеричный адрес символа. Подайте это шестнадцатеричное значение [соответственно приведенное] к BFD, где вы набивали functionname_ptr
Замечания: Вышеуказанное работает, но может быть медленным с повторными вызовами c++filt
Более быстрый способ сделать это состоит в том, чтобы захватить поток данных:
readelf -s <executable> | c++filt
Это также [вероятно] проще сделать таким образом, поскольку вам нужно только проанализировать отфильтрованный вывод и найти подходящее имя.
Кроме того, если у вас есть несколько символов, о которых вы заботитесь, вы можете получить все адреса за один вызов.
Хорошо, я нашел способ. Во-первых, я обнаружил, что bfd
довольно рад обнаружить, что функции-члены отлаживают информацию из указателей-членов, если указатель может быть преобразован в void*
,
Я использовал clang
что не позволило бы мне привести указатель на функцию-член к какому-либо указателю или целому числу.
GCC
позволяет сделать это, но выдает предупреждение.
Существует даже флаг, позволяющий указатель на приведение члена вызываться -Wno-pmf-conversions
,
Имея в виду эту информацию, я сделал все возможное, чтобы преобразовать указатель на функцию-член в void*
и я закончил тем, что делал это, используя союзы.
struct A{
int my_member_function(){return 5.;}
};
union void_caster_t{
int (A::*p)(void) value;
void* casted_value;
};
void_caster_t void_caster = {&A::my_member_function};
_bfd_elf_find_nearest_line (...,
void_caster.casted_value,
...)
в заключение bfd
может дать мне отладочную информацию функции-члена.
Что я еще не понял, так это как получить указатель на конструктор и функции-члены деструктора.
Например
void_caster_t void_caster = {&A::~A};
Выдает ошибку компилятора: «Вы не можете взять адрес деструктора».
Для конструктора я даже не смог найти правильный синтаксис, так как это приводит к ошибке синтаксиса.
void_caster_t void_caster = {&A::A};
Опять же, вся логика, лежащая в основе неспособности, связана с бессмысленными обратными вызовами, но это не так, потому что я хочу, чтобы указатель (или адрес) получал отладочную информацию, а не обратные вызовы.