连接器脚本 - 将一个段放置在内存区域的末尾

23

我已经广泛搜索了如何做这件事情,但没有找到答案。

我的内存布局如下:

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

我将堆放在栈的末尾。堆会增长,而栈是针对我使用的 ARM 芯片的完整降序栈。

现在,我想要将一个单独的部分(称为.persist)放入我的 RAM 内存中。我希望它位于 RAM 的最末尾,并且我想将其编程到我的链接器脚本中。然而,这个 .persist 部分的大小不是由我定义的,而是由编译器从其中包含的符号计算出来的。

到目前为止,我还没有想到一个好办法。因为我知道 RAM 的起始地址和 SIZE,如果我知道部分的大小,计算需要放置该部分的位置将非常简单。然而,根据GNU 链接器文档 (第 74 页),似乎:

SIZEOF(section) 返回命名节的字节数,如果已分配该节。 如果在评估此内容时未分配该节,则链接器将报告错误。

因此,我无法在链接器脚本中确定部分的大小(因为我想在放置/分配它之前计算大小)。

有谁知道好的方法吗?


我有同样的问题。这个链接对你有帮助吗?https://dev59.com/WnfZa4cB1Zd3GeqPRW7A#19348569 - parvus
这是一个部分解决方案,但并不是我想要的。它仍然是其他人帮助过的最接近的方式。谢谢! - nonsensickle
这个问题已经解决了(请看下面我的评论)。 - Magnus Persson
5个回答

7

我曾通过将链接过程分为两步来完成类似的事情。首先,我将有问题的部分编译成自己的目标文件。在我的情况下,我从一个汇编文件生成了一个元数据部分。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 段,我可以在链接器脚本中引用它的大小。

我已经很久没有在这个项目上工作了,但是你的答案似乎最接近实现它。在标记为正确之前,我需要验证一下,但是现在加一。 - nonsensickle
我缺少使这成为可能的链接器脚本。据我所知,在链接器脚本中没有办法引用输入部分的大小,但是这个答案提出了不同的建议... - Matthijs Kooijman

5

我通过使用链接命令: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文件链接。


1
这就是我需要时所做的 --- 它很恶劣,但确实有效。谢谢! - David Given

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 中所有剩余的内存。 - nonsensickle

-1

您可以在特定位置强制使用部分。

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

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

1
是的,我知道这一点。然而,这并不允许我存储我的部分,以便它以特定地址结束。我想要获取我的部分的大小,从一个地址中减去它,得到一个结果地址,然后将其放置在结果地址。这样,我就可以将我的部分(具有可变大小)放置在我的RAM内存块的末尾。我发现这是不可能的,这就是问题所在。 - nonsensickle

-2
我想做的是将一个单独的部分,我们称之为.persist,放入我的RAM内存中。我希望它驻留在RAM的最末端,并且我想将其编程到我的链接器脚本中。
链接器脚本有一个特殊变量叫做Location Counter,它允许修改当前地址,从而通过在地址空间中创建间隙或空洞来修改部分或符号的大小或地址。

是的,我知道这个。你错过了我之后说的关于SIZEOF运算符在你放置它之前不会给你部分大小的事情。我需要在放置之前获得部分的大小才能使其工作。 - nonsensickle
唯一的解决方法是,如果我可以放置该部分,获取其大小,然后取消放置并将其移动到其他位置... - nonsensickle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接