Как получить граф вызывающего из заданного символа в двоичном

Этот вопрос связан с вопрос, который я задал ранее в этот день: Интересно, возможно ли сгенерировать график звонящего из заданной функции (или имени символа, например, взятого из nm), даже если интересующая функция не является частью «моего» исходного кода (например, расположенного в библиотеке, например malloc())

Например знать где malloc используется в моей программе с именем foo Я бы сначала посмотрел имя символа:

nm foo | grep malloc
U malloc@@GLIBC_2.2.5

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

 find_usages foo-with-debug-symbols "malloc@@GLIBC_2.2.5"

Который будет генерировать (текстовый) график звонящего, который я затем смогу обработать дальше.

чтение этот вопрос я нашел radare2 который, кажется, выполняет почти все, что вы можете себе представить, но каким-то образом мне пока не удалось сгенерировать график вызывающего абонента из данного символа ..

Прогресс

С помощью radare2 Мне удалось создать dot Граф вызывающего из исполняемого файла, но что-то по-прежнему не хватает. Я компилирую следующую программу на C ++, которую, я уверен, должен использовать malloc() или же new:

#include <string>

int main() {
auto s = std::string("hello");
s += " welt";
return 0;
}

Я компилирую его со статическими библиотеками, чтобы быть уверенным, что все вызовы, которые я хочу проанализировать, могут быть найдены в двоичном файле:

g++ foo.cpp -static

Запустив nm a.out | grep -E "_Znwm|_Znam|_Znwj|_Znaj|_ZdlPv|_ZdaPv|malloc|free" Вы можете увидеть много символов, которые используются для выделения памяти.

Теперь я бегу radare2 на исполняемом файле:

r2 -qAc 'agCd' a.out > callgraph.dot

С небольшим сценарием (вдохновленный этот ответ) Я ищу путь вызова из любого символа, содержащего «sym.operatornew», но, похоже, его нет!

Есть ли способ убедиться все информация, необходимая для генерации графа вызовов из / в любой функция, которая вызывается внутри этого двоичного файла?

Есть ли лучший способ запустить radare2? Похоже, что разные типы визуализации графа вызовов предоставляют разную информацию — например, художественный генератор ascii предоставляет имена для символов, не предоставляемых генератором точек, в то время как генератор точек предоставляет гораздо больше деталей относительно вызовов.

1

Решение

В общем, вы не можете извлечь точный граф управления потоком из двоичного файла, из-за косвенные прыжки и звонки там. Непрямой вызов машинного кода входит в содержимое какого-либо регистра, и вы не можете надежно оценить все значения, которые может принять регистр (это может быть доказано эквивалентным проблема остановки).

Есть ли способ убедиться, что вся информация, необходимая для генерации графа вызовов из / в любую функцию, которая вызывается внутри этого двоичного файла?

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

Компилятор C ++ (обычно) генерирует косвенные переходы для вызовов виртуальных функций (они переходят через виртуальные таблицы) и, вероятно, при использовании общей библиотеки (читайте Drepper’s Как писать общие библиотеки бумага для большего).

Заглянуть в BINSEC инструмент (разработанный коллегами из CEA, LIST и INRIA), по крайней мере, для поиска ссылок.

Если вы действительно хотите найти большинство (но не все) динамических выделений памяти в исходном коде C ++, вы можете использовать статический анализ исходного кода (например, Frama-С или же Frama-Clang) и другие инструменты, но они не являются серебряной пулей.

Помните, что распределение функций, таких как malloc или же operator new может быть помещен в расположение указателя функции (и ваш код C ++ может иметь некоторые распределитель глубоко где-то похоронен, то вы, скорее всего, косвенный звонки в malloc)

Может быть, вы могли бы потратить месяцы усилий на написание своего Плагин GCC искать звонки malloc после оптимизации внутри компилятора GCC (но обратите внимание, что плагины GCC привязаны к одной конкретной версии GCC). Я не уверен, что оно того стоит. Мой старый (устарел, не поддерживается) GCC MELT проект было в состоянии найти звонки malloc с размером выше некоторой заданной константы. Возможно, по крайней мере через год — конец 2019 года или позже — мой проект-преемник (bismon, финансируется КОЛЕСНИЦА Проект H2020) может быть достаточно зрелым, чтобы помочь вам.

Помните также, что GCC способен к довольно причудливой оптимизации, связанной с malloc, Попробуйте скомпилировать следующий код C

//file mallfree.c
#include <stdlib.h>
int weirdsum(int x, int y) {
int*ar2 = malloc(2*sizeof(int));
ar2[0] = x; ar2[1] = y;
int r = ar2[0] + ar2[1];
free (ar2);
return r;
}

с gcc -S -fverbose-asm -O3 mallfree.c, Вы увидите, что генерируется mallfree.s ассемблерный файл не содержит вызова malloc или free, Такая оптимизация разрешена Как будто правило, и практически полезен для оптимизации большинства случаев использования стандарта C ++ контейнеры.

Так что ты хочешь не просто даже для явно «простого» кода C ++ (и является невозможно в общем случае).

Если вы хотите закодировать плагин GCC и потратить на этот вопрос более года (или могли бы заплатить за это не менее 500 тыс. Евро), свяжитесь со мной. Смотрите также
https://xkcd.com/1425/ (ваш вопрос практически невозможно один).

Кстати, что вас действительно волнует, так это динамическое распределение памяти в оптимизированный код (вы действительно хотите встраивание а также устранение мертвого кода, и GCC делает это довольно хорошо с -O3 или же -O2). Когда GCC вообще не оптимизируется (например, с -O0 которая является неявной оптимизацией), она будет выполнять много «бесполезного» динамического выделения памяти, особенно с кодом C ++ (используя стандартную библиотеку C ++). Смотрите также CppCon 2017: Мэтт Годболт «Что мой компилятор сделал для меня в последнее время? Откручиваем крышку компилятора » говорить.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]