【问题标题】:Bare metal without global operator new没有全球运营商的裸机 new
【发布时间】:2017-01-02 11:57:25
【问题描述】:

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

在所述情况下导致编译失败的最简单方法是删除全局 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使用&lt;array&gt;

有没有办法在全球范围内完全禁止::new

【问题讨论】:

  • 您是否可以接受稍后在链接时出现的错误?
  • C 有时是有充分理由的。这听起来像是其中之一。 C 不会产生隐藏的开销。 new 不是唯一的问题,但惯性也会导致麻烦,例如用于中断处理程序和其他时间关键路径。
  • 这将是 C++ 不常用于此类系统的原因之一。事实上,安全关键软件通常需要使用经过验证的库,这实际上将大部分std:: 排除在普通嵌入式系统编译器之外。
  • @Olaf 继承对中断处理程序几乎没有性能影响。与静态链接函数相比,调用虚拟函数(无论如何在 ARM 上)的开销为 0-1 个周期。
  • @RussSchultz:我在哪里说过开销会是问题?问题是确定性行为。使用虚函数,您可以轻松调用您从未想过甚至没有想过的函数。说:在典型的嵌入式系统上,额外间接的开销非常重要。例如,对于 STM32F,Flash 可能需要超过 8 或 10 个额外的时钟周期。小“缓存”往往无法捕捉到这种情况;更少,因为它是一种数据访问,也会干扰预取。正如我所写:麻烦

标签: c++ c++11 memory-management embedded bare-metal


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    不管你使用什么编程语言:

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

    【讨论】:

    • 此解决方案仅适用于某些平台,其中new 和/或malloc() 取决于此类部分的存在。在很多平台上你不需要那个部分,所有使用 newlib 作为 libc 的系统只需要你实现自己的_sbrk_r() 函数,这与这个部分无关。
    【解决方案3】:

    我要强调两点:

    1. 如果异常没有进入您的代码,则在其定义中包含异常相关的标头不应打扰您。
    2. 当你禁用异常时,你不能在你的代码中使用它们(如果你这样做编译将会失败)。但是,它不会从标准库中删除异常的使用。标准库可能包含模板类的预编译版本,例如std::string(即std::basic_string&lt;char, ...&gt;)或std::streambuf(即std::basic_streambuf&lt;char, ...&gt;),当您在代码中使用它时,编译器不会尝试要实例化模板,它只是重用预编译的版本,但有例外。

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

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

    【讨论】:

    猜你喜欢
    • 2019-08-20
    • 1970-01-01
    • 2020-05-31
    • 2015-07-22
    • 2022-06-10
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多