C 嵌入式链接器脚本-正确放置';堆栈';和';堆';区域?

C 嵌入式链接器脚本-正确放置';堆栈';和';堆';区域?,c,linker,embedded,bare-metal,linker-scripts,C,Linker,Embedded,Bare Metal,Linker Scripts,最近我一直在研究自动生成的STM32项目中使用的链接器脚本,对于堆栈和堆内存段是如何定义的,我有点困惑 例如,我一直在看ST的“CubeMX”固件包中为其F0系列芯片提供的文件,这些芯片具有ARM Cortex-M0内核。如果文件的许可证允许,我会粘贴整个脚本,但是如果您有兴趣,可以从ST免费下载整个包。无论如何,以下是与我的问题相关的部分: /* Highest address of the user mode stack */ _estack = 0x20001000; /* end

最近我一直在研究自动生成的STM32项目中使用的链接器脚本,对于堆栈和堆内存段是如何定义的,我有点困惑

例如,我一直在看ST的“CubeMX”固件包中为其F0系列芯片提供的文件,这些芯片具有ARM Cortex-M0内核。如果文件的许可证允许,我会粘贴整个脚本,但是如果您有兴趣,可以从ST免费下载整个包。无论如何,以下是与我的问题相关的部分:

/* Highest address of the user mode stack */
_estack = 0x20001000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

<...>

SECTIONS {
  <...>

  .bss :
  {
    <...>
  } >RAM

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  <...>
}
/*用户模式堆栈的最高地址*/
_estack=0x2000000;/*闸板端部*/
/*如果堆和堆栈不适合RAM,则生成链接错误*/
_最小堆大小=0x200;/*所需的堆数量*/
_最小堆栈大小=0x400;/*所需的堆栈数量*/
部分{
.bss:
{
}>内存
/*用户\u堆\u堆栈部分,用于检查剩余的RAM是否足够*/
.\u用户\u堆\u堆栈:
{
.=对齐(8);
提供(结束=);
提供(_end=);
.=.+\u最小值\u堆大小;
.=.+\u最小值\u堆栈大小;
.=对齐(8);
}>内存
}
下面是我对链接器行为的错误理解:

  • “_estack”值设置为RAM的末尾-此脚本用于“STM32F031K6”芯片,该芯片具有4KB的RAM,从0x20000000开始。在ST的示例向量表中,它用于定义起始堆栈指针,因此似乎应该用它来标记“堆栈”内存块的一端

  • “\u-Min\u-Heap\u-Size”和“\u-Min\u-Stack\u-Size”值似乎应该定义用于堆栈和堆的最小空间量,以供程序使用。分配大量动态内存的程序可能需要更多的“堆”空间,而调用深度嵌套函数的程序可能需要更多的“堆栈”空间

我的问题是,这应该怎么做?“_Min_x_Space”是特殊的标签,还是这些名称可能有点混淆?因为链接器脚本看起来只是将这些精确大小的内存段附加到RAM中,而没有考虑程序的实际使用情况

此外,为堆栈定义的空间似乎不一定在其开始和上面定义的“_estack”值之间定义连续段。如果没有使用其他RAM,
nm
显示“\u user\u heap\u stack”部分结束于0x20000600,在“\u estack”之前留下一堆空RAM

我能想到的唯一解释是,“Heap”和“Stack”段可能没有实际意义,只定义为编译时保护,这样当动态内存明显少于预期时,链接器就会抛出错误。如果是这样的话,我应该把它看作是一个最小的“组合堆/堆栈”大小吗

或者老实说,如果我的应用程序不使用malloc或它的同类软件,我应该放弃“Heap”段吗?无论如何,在可能的情况下,避免嵌入式系统中的动态内存分配似乎是一种很好的做法

“_estack”值设置为RAM的末尾-此脚本用于“STM32F031K6”芯片,该芯片具有4KB的RAM,从0x20000000开始。在ST的示例向量表中,它用于定义起始堆栈指针,因此似乎应该用它来标记“堆栈”内存块的一端

因为这里的堆栈会向下增长(从高地址到低地址),所以它实际上是堆栈内存区域的开始

“_Min_x_Space”是特殊的标签,还是这些名称可能有点混淆

它们的特殊之处在于,以下划线开头,后跟大写字母的符号是为实现保留的。e、 g.
min\u stack\u space
可能与用户定义的符号冲突

因为链接器脚本看起来只是将这些精确大小的内存段附加到RAM中,而没有考虑程序的实际使用情况

这是最小尺寸。堆栈和堆中断都可能增长

如果没有使用其他RAM,nm将显示“\u user\u heap\u stack”部分结束于0x20000600,这将在“\u estack”之前留下一堆空RAM

它只留下0x400字节,即
\u Min\u Stack\u Size
。请记住,堆栈在此向下生长(通常在其他地方也会向下生长)

不管怎样,在可能的情况下,避免嵌入式系统中的动态内存分配似乎是一种很好的做法


并非每件事都是安全关键的。如果您不想/不需要/不被允许,您可以自由地不使用堆。(好的,在后者中不是那么自由)

您会问一个问题,堆栈和堆应该放在哪里。在uC上,由于许多原因,答案不像@a2f所说的那么明显

堆栈 首先,许多ARM uC有两个堆栈。一个称为主堆栈,另一个称为进程堆栈。当然,您不需要启用此选项

另一个问题是皮层uC可能有(例如STM32F3、许多F4、F7、H7)许多SRAM块。由开发人员决定在何处放置堆栈和堆

在哪里放置堆栈? 我建议将MSP放在所选RAM的开头。为什么? 如果堆栈位于末尾,则无法控制堆栈的使用。当堆栈溢出时,它可能会悄悄地覆盖变量,程序的行为变得不可预测。这不是问题,如果它是LED闪烁的事情。但想象一下,一台大型机器控制器或一辆汽车损坏了电脑

当堆栈将溢出时,将堆栈放置在RAM的开头(作为开始,我指的是RAM开始地址+堆栈大小),将生成硬件异常。您完全控制着uC,您可以看到问题的起因(例如损坏的传感器向uC发送数据),并启动紧急程序(例如停止机器,将车辆置于维修模式等)。书堆