3

我想用 gcc 编译一个带有 ARM 处理器链接时间优化的程序。当我在没有 LTO 的情况下编译时,系统会被编译。当我启用 LTO(使用 -flto)时,我收到以下汇编错误:

错误:无效的文字常量:池需要更近

环顾网络,我发现这与我系统中的常量有关,这些常量放置在一个名为 .rodata 的特殊部分中,该部分称为常量池,位于我系统中的 .text 部分之后。似乎在使用 LTO 编译时,由于内联和其他优化,这个 .rodata 部分离指令太远了,因此不再可能对常量进行寻址。是否可以将常量放在使用它们的函数之后?或者是否可以使用另一种寻址模式,以便仍然可以寻址 .rodata 部分?谢谢。

4

2 回答 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:
于 2014-04-11T15:44:57.543 回答
0

“这是一个汇编程序消息,而不是链接器消息,所以这发生在生成部分之前” - 我不确定,但我认为 LTO 有点复杂。在启用 LTO 的情况下编译(包括组装)单个 c 文件可以正常工作并且不会导致任何问题。当我尝试在启用 LTO 的情况下将它们链接在一起时,就会出现问题。我不知道 LTO 究竟是如何完成的,但显然这还包括再次调用汇编程序,然后我收到此错误消息。在没有 LTO 的情况下进行链接时,一切都很好,当我查看 disassemly 时,我可以看到我的常量没有放在函数之后。相反,所有常量都放在 .rodata 部分中。由于内联而启用了 LTO,我的函数可能会变大以达到常量池......

于 2014-04-15T09:04:09.703 回答