没有全球运营商的裸机新

Bare metal without global operator new

考虑安全软件,其中通常不允许动态分配并且不允许异常。仅当 class 显式定义运算符 newdelete 时才允许动态分配。对其他 class 使用运算符 new 应该会导致编译失败。

在上述情况下导致编译失败的最简单方法是删除全局新运算符:

void* operator new(std::size_t) = delete;

一方面,这会对标准库造成副作用。例如,包含 <array> 通过 <stdexcept> 将包含传播到 <new_allocator><new_allocator> 使用 ::new 运算符,即使您不想使用异常和内存分配,这也会导致构建失败。根据 Scoot Meyers <array> 应该是裸机友好的。

另一方面,这会导致编译器内置运算符出错

src/main.cpp:91:31: error: deleted definition of 'void* operator new(std::size_t)'
 void* operator new(std::size_t) = delete;                               ^
<built-in>: note: previous declaration of 'void* operator new(std::size_t)'

有什么办法可以禁止 ::new 而使用 <array> 吗?

是否有任何解决方案可以在全球范围内 完全禁止 ::new

无论您使用何种编程语言:

在任何健全的裸机系统上,您只需从 linker 脚本中完全删除 .heap 段。任何依赖动态分配的代码都将无法 link。而且您不必为您无论如何都不会使用的段分配 RAM。

如果您使用 GCC 和 GNU LD,那么我认为您可以将 --wrap=malloc 添加到您的链接器标志中。由于全局 ::new 在内部使用 malloc(),因此在您的应用程序中对 malloc() 的所有调用都将替换为 __wrap_malloc()。如果此函数未定义,则链接将失败。

另一个可能更简单的选项是将 ASSERT(DEFINED(malloc) == 0, "Dynamic allocation used!"); 添加到您的链接描述文件中。这将断言 malloc() 未定义。

这些选项都不能防止您重新定义全局 ::new 以使用某种其他形式的全局分配。您可以在链接描述文件中对全局符号 ::new 执行相同的操作,但它的名称被破坏了(在此处 _Znwj),所以这会有点奇怪...

有两点我想强调一下:

  1. 如果异常没有在您的代码中找到它们的方式,那么包含异常相关的 headers 及其定义不应该打扰您。
  2. 禁用异常后,您将无法在代码中使用它们(如果这样做,编译将失败)。但是,它不会从标准库中删除异常的使用。标准库可能包含 pre-compiled 版本的模板 类,例如 std::string(即 std::basic_string<char, ...>)或 std::streambuf(即 std::basic_streambuf<char, ...>),以及当您在代码中使用它时,编译器不会尝试实例化模板,它只是重用 pre-compiled 版本,但有例外。

为了更好地控制您的代码,我强烈建议使用 G++ 编译器的 -nostdlib 编译选项完全排除标准库。它不会阻止您使用各种模板 类,例如来自 STL 的 std::array,它只会为您排除整个 C++ 库和运行时。

我也推荐阅读Practical Guide to Bare Metal C++。它可能会更深入地了解 C++ bare-metal 内部结构。