【问题标题】:Linker Script - Placing a section at the end of a memory region链接器脚本 - 在内存区域的末尾放置一个节
【发布时间】:2013-09-09 22:51:57
【问题描述】:

我已经广泛搜索了如何做到这一点,但未能找到答案。

我的内存布局如下:

Fake Address | Section
     0       |  text
     7       |  relocate
    15       |  bss
    23       |  stack

在堆栈的末尾,我放置了堆。它长大了,堆栈是我正在使用的 ARM 芯片的完整降序堆栈。

现在,我要做的是在我的 RAM 内存中放置一个单独的部分,我们称之为 .persist。我希望它位于 RAM 的最末端,并且我想将它编程到我的链接器脚本中。但是,.persist 部分的大小不是由我定义的,而是由编译器根据它包含的符号计算得出的。

到目前为止,我还没有想出一个好的方法来做到这一点。因为我知道 RAM 起始地址和 SIZE,所以如果我知道部分大小,计算部分需要去的位置将是微不足道的。但是,根据the GNU linker documentation (pg 74) 看来:

SIZEOF(section) 返回命名的字节大小 部分,如果该部分已被分配。 如果在评估时该节尚未分配,则链接器将 报错。

所以我无法计算链接描述文件中部分的大小(因为我想在放置/分配它之前计算大小)。

有谁知道这样做的好方法吗?

【问题讨论】:

  • 我也有同样的问题。这对你有帮助吗? stackoverflow.com/a/19348569/911550
  • 这是一个部分解决方案,但不是我所追求的。它仍然是最接近其他人的帮助。谢谢!
  • 这已经解决了(见我下面的评论)。

标签: c++ c memory linker linker-scripts


【解决方案1】:

我能够通过两步过程来完成类似的事情。 首先,我将相关部分编译为它自己的目标文件。就我而言,我有一个从程序集文件生成的元数据部分。 gcc -c 会将源代码编译成目标文件,但不会链接它们。

gcc -c  metadata.s  -o metadata.o

您也可以构建整个程序,然后使用objcopy 仅提取有问题的部分。

gcc -c  main.cc  -o main.o
objcopy --only-section=.metadata  main.o  metadata.o

现在我构建并链接程序的其余部分,并将目标文件包含在链接器的输入中。

gcc metadata.o  ../main.o  -o Program.elf  -T linkerscript.ld   

链接器从目标文件中读取.metadata部分,我可以在链接器脚本中引用它的大小。

【讨论】:

  • 自从我上次从事这个项目以来已经有很长时间了,但您的回答似乎最接近于使之成为可能。我必须在标记为正确之前对其进行验证,但现在 +1。
  • 我缺少使这成为可能的链接器脚本。 AFAICS,没有办法在链接器脚本中引用 input 部分的大小,但这个答案表明否则......
【解决方案2】:

我设法通过使用链接器命令计算代码的大小来解决它:size。 在我的 Makefile 中,我将 SIZE 设置为代码的大小。然后我调用 cpp(预处理器)来计算所有绝对地址(使用 c 语法)。然后我使用生成的链接文件进行链接:tmp.ld

%.elf: %.o
    $(eval SIZE := $(shell arm-none-eabi-size -B $<  | tail -n 1 | awk -F ' ' '{print $$1}'))
    $(CC) -DSEG_SIZE=$(SIZE) -P -E -x c link.ld -o tmp.ld
    $(CC) -o $@ $< $(LDFLAGS)

在 link.ld 文件中我可以进行各种计算(因为 SEG_SIZE 是一个常数):

#define SEG_LAST_ADDR 1234
#define MY_SEG        (SEG_LAST_ADDR - SEG_SIZE)

MEMORY
{
  bootloader     (rx)  : ORIGIN = MY_SEG,     LENGTH = SEG_SIZE
  ...
}

我终于链接到了 tmp.ld 文件。

【讨论】:

    【解决方案3】:

    我遇到了类似的问题 我是这样做的

    /* heap section */
    .heap (NOLOAD):
    {
        . = ALIGN(8);
         _sheap = .;
        . = . + HEAP_SIZE;
        . = ALIGN(8);
        _eheap = .;
    } > ram
    
    _ram_end_ = ORIGIN(ram) + LENGTH(ram) -1 ;
    _stack_size = _ram_end_ - _eheap ;
    
    /* stack section */
    .stack (NOLOAD): 
    {
        . = ALIGN(8);
        _sstack = .;
        . = . + _stack_size;
        . = ALIGN(8);
        _estack = .;
    } > ram
    
    .LastSection (NOLOAD): /* for test in dump file */
    {
        . = ALIGN(8);
    } > ram
    

    【讨论】:

    • 虽然我确实考虑过这一点,但问题是要求在 ram 的末尾放置一个部分,以便该部分的最后一个字节占据_ram_end 地址。从我所见,您将堆栈放置在 ram 的末尾,堆栈仅占用所有剩余字节。我要求使用与堆栈不同的部分来执行此操作,该部分具有不依赖于其他部分大小的静态大小。实际上,在此示例中,您的堆栈占用了 RAM 中的所有剩余内存。
    【解决方案4】:

    您可以在特定位置强制执行部分。

    例如,在这个 Red Hat GNU Linker 文档 page 中,您可以将 .data 部分定义为从地址 0x8000000 开始:

    SECTIONS
    {
      . = 0x10000;
      .text : { *(.text) }
      . = 0x8000000;
      .data : { *(.data) }
      .bss : { *(.bss) }
    }
    

    【讨论】:

    • 是的,我知道这一点。但是,这不允许我存储我的部分,以便它在特定地址结束。我想得到我的部分的大小,从给我结果地址的地址中减去它,然后将它放在结果地址。通过这种方式,我可以将具有可变大小的部分放在我的 RAM 内存块的末尾。我发现这是不可能的,这就是问题所在。
    【解决方案5】:

    我想要做的是在我的 ram 内存中放置一个单独的部分,我们称之为 .persist。我希望它驻留在 RAM 的最末端,并且我想将它编程到我的链接器脚本中。

    链接器脚本有一个名为Location Counter 的特殊变量,它允许通过在地址空间中创建间隙或孔来修改当前地址,以及节或符号的大小或地址。

    【讨论】:

    • 是的,我知道这一点。你错过了我在SIZEOF 操作符没有给你直到放置它的部分的大小之后所说的话。我需要在 之前获取部分的大小,以便让它工作。
    • 唯一的解决方法是如果我可以放置该部分,获取大小,然后取消放置并将其移动到其他地方......
    猜你喜欢
    • 1970-01-01
    • 2015-02-26
    • 2020-11-15
    • 2015-02-08
    • 1970-01-01
    • 2012-04-26
    • 2012-10-22
    相关资源
    最近更新 更多