Как статически определить динамическое распределение кучи?
Я собираюсь удалить "как можно больше" динамического распределения кучи в моем приложении, и мне интересно, как я могу убедиться, что ничего не пропустил.
В настоящее время я ищу способ легко или даже автоматически определить, могут ли какие-либо (или какие) части кода вызывать стандартные реализации new
/delete
или же malloc
/free
без необходимости динамического отслеживания распределений (т. е. с помощью статического анализа кода или обратной связи от компилятора / компоновщика).
Легко обнаружить (или найти) код, который напрямую вызывает new
или же malloc
конечно:
int main() {
auto s = new std::string();
delete s;
}
На случай, если выделения скрыты глубоко в сторонней библиотеке или в случаях, которые менее очевидны (например, throw
) Я все еще могу искать искаженные символы для нового / удаления в моем двоичном файле:
g++ main.cpp
nm a.out | grep -E "_Znwm|_Znam|_Znwj|_Znaj|_ZdlPv|_ZdaPv|malloc|free"
U _ZdlPvm@@CXXABI_1.3.9
U _Znwm@@GLIBCXX_3.4
Но этот подход найдет только прямое использование new/delete/malloc/free
, В случае, если мой код (или материал сторонних разработчиков) использует стандартную библиотеку, вы не обнаружите ее, просто позвонив nm
:
int main() {
std::string a;
for(int i = 0; i < 100; ++i) {
a += "data ";
}
}
Мой текущий подход состоит в том, чтобы связать статические стандартные библиотеки (-static-libgcc
/ -static-libstdc++
) и посмотрите, есть ли ссылка на new
/delete
вообще на весь двоичный файл:
g++ -static-libgcc -static-libstdc++ main.cpp
nm a.out | grep -E "_Znwm|_Znam|_Znwj|_Znaj|_ZdlPv|_ZdaPv|malloc|free"
0000000000471fd0 T __cxa_free_dependent_exception
0000000000471f30 T __cxa_free_exception
U free@@GLIBC_2.2.5
U __freelocale@@GLIBC_2.2.5
U malloc@@GLIBC_2.2.5
0000000000471b20 T _ZdlPv
0000000000491bf0 T _ZdlPvm
0000000000471bc0 t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.2
0000000000402a20 t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.2.cold.5
0000000000471e80 T _ZN9__gnu_cxx9__freeresEv
0000000000472240 T _Znwm
0000000000491c00 T _ZnwmRKSt9nothrow_t
0000000000403f37 t _ZnwmRKSt9nothrow_t.cold.0
Этот подход работает для небольших двоичных файлов, где вы можете иметь нулевое выделение кучи, но как только вы захотите разрешить некоторые выделения (например, в коде, который выполняется только один раз, или в случаях ошибок, становится очень трудно, следовательно, отделить код, который может выделить кучу память и вещи, которые не будут.
В лучшем случае я могу себе представить, что компилятор или статический анализатор кода предоставляет мне список мест в моем исходном коде, которые могут привести к динамическому выделению кучи. Этот список я мог бы регулярно проверять / фильтровать для случаев, которые в порядке в моей настройке (например, загрузочный код или обработка ошибок) и те, где я должен рефакторинг (например, путем предоставления специального распределителя).
Каковы ваши подходы, инструменты, опыт?
1 ответ
Одна стратегия может заключаться в том, чтобы обернуть вызовы malloc/calloc своей собственной функцией, которая решает, разрешены ли они, и утверждает (или регистрирует), если нет. Решение может быть принято путем проверки глобального флага, который вы устанавливаете / сбрасываете во время инициализации, обработчиков ошибок и т. Д.
Это, конечно, не статично, но может послужить хорошей отправной точкой для дезинфекции вашего кода.