Как статически определить динамическое распределение кучи?

Я собираюсь удалить "как можно больше" динамического распределения кучи в моем приложении, и мне интересно, как я могу убедиться, что ничего не пропустил.

В настоящее время я ищу способ легко или даже автоматически определить, могут ли какие-либо (или какие) части кода вызывать стандартные реализации 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 своей собственной функцией, которая решает, разрешены ли они, и утверждает (или регистрирует), если нет. Решение может быть принято путем проверки глобального флага, который вы устанавливаете / сбрасываете во время инициализации, обработчиков ошибок и т. Д.

Это, конечно, не статично, но может послужить хорошей отправной точкой для дезинфекции вашего кода.

Другие вопросы по тегам