问题描述
使用 MinGW-w64 附带的 GNU Assembler 版本组装由我的编译器生成的 x86 汇编代码会产生以下警告:
E:/MinGW32/mingw32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/bin/ld.exe: analogClockForWindows.exe: warning: section .bss: alignment 2**32 not representable
这是什么意思?最让我疑惑的是汇编代码根本没有提到.bss
。
不知道有没有关系,但是CygWin自带的GNU Assembler版本和TDM自带的版本-GCC 甚至拒绝组装我的代码,并给出大量错误消息。
我的编译器生成的汇编代码可在此处获得:https://github.com/FlatAssembler/ArithmeticExpressionCompiler/files/6446315/analogClockForWindows.s.zip
编辑:我只是使用 gcc -o analogClockForWindows analogClockForWindows.s
编译 my compiler 生成的汇编代码。
更新:我认为这是触发该警告的最小示例:
.text
.comm output,7360,32
.global _main
_main:
movl $0,%eax
ret
解决方法
https://sourceware.org/binutils/docs/as/Comm.html#Comm
当使用 ELF 或(作为 GNU 扩展)PE 时,.comm 指令采用可选的第三个参数。这是符号所需的对齐方式,对于 ELF 指定为字节边界(例如,16 的对齐方式意味着地址的最低有效 4 位应为零),而对于 PE 指定为 2 的幂(例如,对齐 5 表示对齐到 32 字节边界)。
ELF 是 Linux 上使用的可执行格式,PE 是 Windows 上使用的。因此,对于 ELF (Linux),.comm output,7360,32
请求将 output
对齐到 32 字节边界,这很好。但是对于 PE (Windows),相同的指令被解释为要求 2**32
字节对齐,这是不可能的。在 Windows 上,您应该改为编写 .comm output,5
。
我不知道为什么这些不同;可能是为了与其他不同的汇编器兼容。
您可能需要向编译器添加一个标志以在 Windows 和 Linux 之间进行选择,以便可以为每种情况输出正确的指令。
,这可能意味着您使用了 .align 32
,它被解释为 .p2align
(2 的幂)而不是 .balign
(字节)。
在定位支持明确指令的 GAS 时,切勿使用歧义 .align
指令。
在您的情况下,对齐出现在第三个参数 .comm
中,以在 BSS 中定义全局变量。像 .align
一样,解释依赖于目标,当为 Windows 组装时,这意味着您要求它通过 2**32
(或 4GiB)对齐,ld
不处理.
(为 i386-elf-linux 组装时,表示按 32 对齐,2**5
,按预期工作。)
如 the GAS manual 中所述:
当使用 ELF 或(作为 GNU 扩展)PE 时,.comm 指令采用可选的第三个参数。这是符号所需的对齐方式,将 ELF 指定为字节边界(例如,16 的对齐方式意味着地址的最低有效 4 位应为零),并且 对于 PE 为 2 的幂(例如,5 对齐表示对齐到 32 字节边界)。
对齐必须是绝对表达式,并且必须是2的幂。如果 ld 为公共符号分配了未初始化的内存,它会在放置符号时使用对齐方式。
如果没有指定对齐方式,as会将对齐方式设置为小于或等于符号大小的最大2的幂,在ELF上最多为16,或者在PE上默认为4的部分对齐方式
不幸的是,没有可移植的明确语法方式来使用它。您可以省略可选的第三个参数并使用默认对齐方式。
或者您可以改为使用 .bss
指令切换到 BSS 部分并使用 .space
在 label:
后保留空间。
或者只是不使用任何全局变量,并将结果保留在 st0
中。如果您在堆栈中使用 args,则不需要任何变量来评估表达式的全局变量。如果 x87 寄存器用完,您最多可以使用堆栈空间。或者,您可以引用全局变量,但将定义留给其他编译单元(例如,执行输入/输出的调用方。)
另外,result
真的需要 7360 字节吗?您的编译器 (https://flatassembler.github.io/compiler.html) 只使用 [result]
,而不使用 [result + reg]
或 + constant