在内存中,栈底和堆底的地址应该一样吗?
In memory, should stack bottom and heap bottom have the same address?
我正在使用带有 this 链接器脚本的 tm4c123gh6pm MCU。走到底部,我看到:
...
...
.bss (NOLOAD):
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM
_heap_bottom = ALIGN(8);
_heap_top = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size;
_stack_bottom = ALIGN(8);
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM);
看来堆底和栈底是一样的。我已经仔细检查过了:
> arm-none-eabi-objdump -t mcu.axf | grep -E "(heap|stack)"
20008000 g .bss 00000000 _stack_top
20007000 g .bss 00000000 _heap_top
00001000 g *ABS* 00000000 _stack_size
20000558 g .bss 00000000 _heap_bottom
20000558 g .bss 00000000 _stack_bottom
是否正确?据我所知,堆栈可能会覆盖堆,是这种情况吗?
如果我刷新这个 FW 'works'(至少现在是这样),但是如果堆栈足够大并且我使用动态内存,我预计它会失败。我观察到我的代码或启动脚本中没有人使用堆栈和底部符号,所以即使我使用堆栈和堆,一切都可以继续工作。 (除非栈和堆是我看不到的人用的特殊符号,是这样吗?)
我想通过以下方式更改最后一部分:
_heap_bottom = ALIGN(8);
_heap_top = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size;
_stack_bottom = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size + 4; // or _heap_top + 4
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM);
以上是否正确?
如果您编写自己的链接描述文件,那么堆栈和堆的排列方式由您决定。
一种常见的方法是将堆栈和堆放在同一个块中,堆栈从最高地址向最低地址向下增长,而堆从较低地址向最高地址向上增长。
这种方法的优点是你不需要单独计算你需要多少堆或栈。只要任一时刻使用的堆栈和堆的总和小于可用的总内存,那么一切都可以。
这种方法的缺点是当你分配的内存比你拥有的多时,你的堆栈会溢出到你的堆中,反之亦然,你的程序会以各种方式失败,这很难预测或确定它们何时发生。
您问题中的链接描述文件使用了这种方法,但似乎有一个错误,详情如下。
请注意,在谈论 ARM 上的堆栈时使用名称 top 和 bottom 是非常无用的,因为当您将某些内容压入堆栈时堆栈指针的数值减少。当堆栈为空时,堆栈指针的值最高,当堆栈满时,堆栈指针的值最低。 top是指最高地址还是当前指针所在位置,bottom是指最低地址还是push第一项的地址,不明确
在 CMSIS 示例链接器脚本中,堆的下限和上限称为 __heap_base
和 __heap_limit
,下限 和 堆栈的上边界分别称为__stack_limit
和__initial_sp
。
在此脚本中,符号具有以下含义:
_heap_bottom
是堆的最低地址。
_heap_top
是堆不能增长的上限地址,如果你想为堆栈保留至少 _stack_size
字节。
对于 _stack_bottom
,似乎脚本作者可能错误地认为 ALIGN(8)
会对齐最近分配的值,因此他们希望 _stack_bottom
是对齐的版本_heap_top
,当 _stack_size
字节被推送到它时,它将成为堆栈指针的值。事实上 ALIGN(8)
对齐 .
的值,正如您观察到的那样,它仍然与 _heap_bottom
具有相同的值。
最后_stack_top
是内存中的最高地址,也就是栈为空时栈指针的起始值。
如果堆栈限制的值不正确,几乎肯定不会有任何作用,因为这个符号可能从未在代码中使用过。在这个 ARMv7M 处理器上,push 和 pop 指令以及硬件对堆栈的其他访问都假定堆栈是无限资源。使用所有正常 ABI 的编译器也会生成在堆栈增长之前不进行检查的代码。这样做的原因是它是最常执行的操作之一,因此添加额外的指令会削弱性能。不过,下一代 ARMv8M 确实具有对堆栈限制寄存器的硬件支持。
我给你的建议是删除该行。如果有任何东西在使用它,那么您基本上就失去了共享堆栈和堆的全部好处 space。如果您确实要计算并检查它,那么您的建议是正确的,只是您不需要添加 + 4
。这将创建一个 4 字节的间隙,该间隙不能用作堆或堆栈。
顺便说一句,我个人更喜欢将堆栈放在内存的底部,将堆放在顶部,彼此远离。这样,如果它们中的任何一个变得比它们应该的更大,它们就会进入一个未分配的地址 space,可以将其配置为立即导致总线故障,而无需任何软件一直检查这些值。
我正在使用带有 this 链接器脚本的 tm4c123gh6pm MCU。走到底部,我看到:
...
...
.bss (NOLOAD):
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > SRAM
_heap_bottom = ALIGN(8);
_heap_top = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size;
_stack_bottom = ALIGN(8);
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM);
看来堆底和栈底是一样的。我已经仔细检查过了:
> arm-none-eabi-objdump -t mcu.axf | grep -E "(heap|stack)"
20008000 g .bss 00000000 _stack_top
20007000 g .bss 00000000 _heap_top
00001000 g *ABS* 00000000 _stack_size
20000558 g .bss 00000000 _heap_bottom
20000558 g .bss 00000000 _stack_bottom
是否正确?据我所知,堆栈可能会覆盖堆,是这种情况吗?
如果我刷新这个 FW 'works'(至少现在是这样),但是如果堆栈足够大并且我使用动态内存,我预计它会失败。我观察到我的代码或启动脚本中没有人使用堆栈和底部符号,所以即使我使用堆栈和堆,一切都可以继续工作。 (除非栈和堆是我看不到的人用的特殊符号,是这样吗?)
我想通过以下方式更改最后一部分:
_heap_bottom = ALIGN(8);
_heap_top = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size;
_stack_bottom = ORIGIN(SRAM) + LENGTH(SRAM) - _stack_size + 4; // or _heap_top + 4
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM);
以上是否正确?
如果您编写自己的链接描述文件,那么堆栈和堆的排列方式由您决定。
一种常见的方法是将堆栈和堆放在同一个块中,堆栈从最高地址向最低地址向下增长,而堆从较低地址向最高地址向上增长。
这种方法的优点是你不需要单独计算你需要多少堆或栈。只要任一时刻使用的堆栈和堆的总和小于可用的总内存,那么一切都可以。
这种方法的缺点是当你分配的内存比你拥有的多时,你的堆栈会溢出到你的堆中,反之亦然,你的程序会以各种方式失败,这很难预测或确定它们何时发生。
您问题中的链接描述文件使用了这种方法,但似乎有一个错误,详情如下。
请注意,在谈论 ARM 上的堆栈时使用名称 top 和 bottom 是非常无用的,因为当您将某些内容压入堆栈时堆栈指针的数值减少。当堆栈为空时,堆栈指针的值最高,当堆栈满时,堆栈指针的值最低。 top是指最高地址还是当前指针所在位置,bottom是指最低地址还是push第一项的地址,不明确
在 CMSIS 示例链接器脚本中,堆的下限和上限称为 __heap_base
和 __heap_limit
,下限 和 堆栈的上边界分别称为__stack_limit
和__initial_sp
。
在此脚本中,符号具有以下含义:
_heap_bottom
是堆的最低地址。
_heap_top
是堆不能增长的上限地址,如果你想为堆栈保留至少 _stack_size
字节。
对于 _stack_bottom
,似乎脚本作者可能错误地认为 ALIGN(8)
会对齐最近分配的值,因此他们希望 _stack_bottom
是对齐的版本_heap_top
,当 _stack_size
字节被推送到它时,它将成为堆栈指针的值。事实上 ALIGN(8)
对齐 .
的值,正如您观察到的那样,它仍然与 _heap_bottom
具有相同的值。
最后_stack_top
是内存中的最高地址,也就是栈为空时栈指针的起始值。
如果堆栈限制的值不正确,几乎肯定不会有任何作用,因为这个符号可能从未在代码中使用过。在这个 ARMv7M 处理器上,push 和 pop 指令以及硬件对堆栈的其他访问都假定堆栈是无限资源。使用所有正常 ABI 的编译器也会生成在堆栈增长之前不进行检查的代码。这样做的原因是它是最常执行的操作之一,因此添加额外的指令会削弱性能。不过,下一代 ARMv8M 确实具有对堆栈限制寄存器的硬件支持。
我给你的建议是删除该行。如果有任何东西在使用它,那么您基本上就失去了共享堆栈和堆的全部好处 space。如果您确实要计算并检查它,那么您的建议是正确的,只是您不需要添加 + 4
。这将创建一个 4 字节的间隙,该间隙不能用作堆或堆栈。
顺便说一句,我个人更喜欢将堆栈放在内存的底部,将堆放在顶部,彼此远离。这样,如果它们中的任何一个变得比它们应该的更大,它们就会进入一个未分配的地址 space,可以将其配置为立即导致总线故障,而无需任何软件一直检查这些值。