【发布时间】:2019-03-20 20:01:51
【问题描述】:
我即将在我的应用程序中删除“尽可能多”的动态堆分配,我想知道如何确保我没有遗漏任何东西。
目前,我正在寻找一种方法来轻松甚至自动判断代码的任何(或哪些)部分是否可能调用 new/delete 或 malloc/free 的标准实现而不必须动态跟踪分配(即通过静态代码分析或来自编译器/链接器的反馈)。
当然,很容易发现(或搜索)直接调用new 或malloc 的代码:
int main() {
auto s = new std::string();
delete s;
}
万一分配隐藏在第 3 方库的深处或不太明显的情况(如 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。如果我的代码(或第 3 方的东西)使用标准库,你不会通过调用 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
这种方法适用于小型二进制文件,您可以在其中管理 零 堆分配,但只要您想允许 一些 分配(例如,在仅执行的代码中一旦或在错误情况下,很难区分可能分配堆内存的代码和不会分配堆内存的代码。
在我目前可以想象的最佳情况下,编译器或静态代码分析器会为我提供源代码中可能导致动态堆分配的位置列表。这个列表我可以定期检查/过滤在我的设置中正常的情况(例如引导代码或错误处理)以及我必须重构的情况(例如通过提供特殊分配器)。
您的方法、工具和经验是什么?
【问题讨论】:
-
您确实需要以某种方式解决问题。在第三方库中寻找动态内存分配的使用是一个棘手的问题,请给出实现选择的范围以及库在您控制之外进行更新的可能性。通常,从头开始设计程序以避免动态内存分配(或至少限制对某些特定代码的使用)比在重要的现有程序中查找实例更容易并删除它们。
-
这很难找到。我过去所做的是从链接器文件中完全消除
.heap段。如果在那之后链接器阶段有什么东西在尖叫,那么某些库中有一些隐藏的堆代码。长期的解决方案是移植到 C,因为它更适合嵌入式编程。
标签: c++ embedded heap-memory