ARM + gcc:不要使用一个大的.rodata段

3
我想使用gcc编译一个针对ARM处理器的程序,并启用链接时优化。当我不使用LTO进行编译时,系统能够被编译。但是当我启用LTO(使用-flto)时,我会遇到以下汇编错误:

Error: invalid literal constant: pool needs to be closer

通过在网上查找,我发现这与我的系统中的常量有关,这些常量位于名为.rodata的特殊部分中,该部分称为常量池,并位于我的系统中的.text部分之后。似乎在使用LTO进行编译时,由于内联和其他优化,.rodata部分距离指令太远,以至于无法再寻址常量。是否可以将常量放置在使用它们的函数之后?或者是否可以使用另一种寻址模式来寻址.rodata部分?谢谢。

常量不一定要在.rodata中。通常,它们直接放在函数后面。如果您知道这是发生在哪里的(我们需要更多上下文),我或许能够提供更好的帮助。例如,如果您没有内联汇编,则在特定有问题的函数上使用__attribute__((noinline))。如果它在内联汇编中,则可以使用其他技术来处理它。-flto相对较新。编译器可以定位分支(例如b 1f)并放置文字。但是,在当前形式中,它可能只会将它们放在函数的末尾。 - artless noise
例如,我相信使用-flto选项后,您的函数将变得大于4k,并且函数的结尾不在ldr rX,[pc,#immed_12]范围内。对于资源受限的系统,这也可能是不好的,因为内联会增加堆栈使用。如果可能,请提供有关ARM目标的更多信息(Thumb2?)。 - artless noise
一些其他的可能性,如 -finline-limit=2048--param max-inline-insns-auto=200--param max-inline-insns-single=200 等等。基本上,告诉 -flto 限制函数的大小。 - artless noise
当然,这个文件会立即被删除,所以我无法查看它……有没有办法告诉GCC不要删除这些临时文件? - user3035952
我的目标是Cortex-A9,内存对于我的系统来说真的不是问题,无论是程序内存还是数据内存。这一切都关乎性能。实际上,我想使用LTO的主要原因是我希望尽可能地进行内联。 - user3035952
显示剩余4条评论
2个回答

1
这是一个汇编器的信息,而不是链接器的信息,在生成节区之前发生。
汇编器有一个伪指令,用于将常量加载到寄存器中:
    ldr r0, =0x12345678

这被扩展为:
    ldr r0, [constant_12345678, r15]
    ...
    bx lr
constant_12345678:
    dw 0x12345678

常量池通常跟随返回指令。通过函数内联,函数可能变得足够长,以至于返回指令太远;不幸的是,编译器无法知道内存地址之间的距离,汇编器除了“流程不会超过返回指令,因此在此处发出常量池是安全的”之外,没有其他控制流信息。
不幸的是,目前还没有好的解决方案。
你可以尝试包含一个asm块。
    b 1f
    .ltorg
1:

这将在此处强制发出常量池,代价是多一个分支指令。 可能可以指示汇编器在常量池为空时省略分支,但我现在无法测试,所以这可能无效。
    .if (2f - 1f)
    .b 2f
    .endif
1:
    .ltorg
2:

GCC不使用那个伪指令,所以它必须在某个内联汇编中。 - Timothy Baldwin

0
"这是一个汇编器消息而不是链接器消息,因此它发生在生成段之前。我不确定,但我认为启用LTO会更加复杂。启用LTO的单个C文件的编译(包括汇编)正常工作且不会引起任何问题。问题出现在我尝试使用启用LTO链接它们在一起时。我不知道LTO的具体实现方式,但显然这也包括重新调用汇编器,然后我得到了这个错误消息。当不使用LTO链接时,一切都很好,当我查看反汇编时,我可以看到我的常量不是放在函数之后,而是放在.rodata部分中。由于内联,启用LTO后,我的函数可能太大而无法访问常量池..."

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