MARS MIPS 模拟器的内置汇编程序比要求的对齐更多?

MARS MIPS simulator's built-in assembler aligns more than requested?

我有以下数据段

.data
a:  .byte   0x11
    .align  1
b:  .word   0x22334455

假设地址 "a" 为 0x10010000,则 b 处单词的预期地址为 0x10010002,但 MARS 将单词存储在 0x10010004,忽略显式的“.align”指令。顺便说一下,我使用 MARS MIPS 模拟器(MacBook Pro 上的版本 4.5)来 assemble 上面的代码。

因此,我的问题是:这是一个错误,还是预期 MARS 的行为不同于 SGI 1992 年的 MIPS 汇编语言文档,例如Page 8-1 of this Pascal / Assembly manual?

(MARS 和非 MARS MIPS asm 文档同意 MIPS 语法中的 .align 采用 2 的幂 arg,因此 .align 1 对齐到 2^1 = 2 字节边界. 不像 GAS / Unix assembler 一些其他架构的语法,其中 .align = 字节对齐,其中 1 的 arg 是多余的。)

TL:DR:MARS 工具提示具有误导性;您需要使用 .align 0 为该部分的其余部分禁用自动对齐。你不能只是下一个词欠对齐。


.align 1 确实按 2 对齐,这不是问题所在。例如在 .byte.ascii 伪指令之间尝试。

例如此源生成 0x00110062 作为 .data 部分的第一个单词,就像 .byte 'b', 0, 0x11, 0 一样。

.data
  a:   .ascii "b"
  b:
      .align 1
      .byte   0x11

并且 b: 标签在 对齐填充后具有地址 2

(为了简单起见,我将 MARS 设置为 "compact" 内存布局,数据部分从地址 0 开始。)


目前我们所看到的确实与您为他们的 Unix assembler 链接的 Silicon Graphics 文档相匹配。 (这与 GNU as(又名 GAS)和 clang 等现代 assemblers 的工作方式截然不同。)

SGI 文档说:

Advance the location counter to make the expression low order bits of the counter zero. Normally, the .half, .word, .float, and .double directives automatically align their data appropriately. For example, .word does an implicit .align 2 (.double does an .align 3). You disable the automatic alignment feature with .align 0. The assembler reinstates automatic alignment at the next .text, .data, .rdata, or .sdata directive.

Labels immediately preceding an automatic or explicit alignment are also realigned. For example, foo: .align 3; .word 0 is the same as .align 3; foo: .word0.

这并没有说明使用 .align 1 下对齐下一个 .word。只是您可以使用 .align 0 完全关闭隐式对齐作为数据指令的一部分。让 .align 1 覆盖和欠对齐下一个 .word 而不必禁用自动对齐是有意义的并且是一个有效的设计,但这不是他们选择实现的功能。

(请注意 .align 0 是特殊的:按 1 个字节对齐永远不必插入任何填充;当前位置始终是字节边界。因为没有理由使用 .align 0 进行对齐一个位置,语法的设计者可以用不同的含义重载它:禁用自动对齐。)

MARS 确实支持这一点。(并且 然后 .align 1 会做你期望的,对齐到 2^1 = 2 没有隐式 .align 2 作为 .word 的一部分,之后增加对齐。)

a:   .byte 1
 .align 1
b:
 .align 0              # on this line or any earlier line
 .word   0x22334455

 .word   0x66666666    # this word is also misaligned; auto-align is disabled

数据部分输出:

0x44550001    0x66662233    0x00006666     as little-endian words
01 00 55 44   33 22 66 66   66 66 00 00    as bytes

是的,.align(明确地或作为 .word 的一部分)不只是在当前位置插入填充,而是在 之前插入它任何前面的标签,紧跟在最后一条数据之后。

如果你真的想避免隐式对齐到 4 字节边界,而不禁用自动对齐,你当然可以使用 .byte.half 指令发出任何你想要的数据。您通常实际上并不需要它,并且在大多数情况下它可以避免初学者遇到对齐问题。 MIPS 是一个严重面向字的 ISA,因此通常没有理由让 .word.

对齐不足

我看到的唯一 MARS 错误是可用性:一个非常具有误导性的工具提示。

它目前表示在指定的字节边界上对齐下一个数据项:(0=字节,1=半,2=字,3=双)。这似乎意味着您可以欠对齐 .word。而且它对 .align 0 具有高度误导性,它实际上禁用了该部分其余部分的自动对齐。


这不是 .align 在使用 GAS 语法(GNU as 或 clang)的 assemblers 中的工作方式。(例如,参见the GAS manual)

在我的 Linux 桌面上,我使用 clang -c -target mipsel mips-align.s assembled 你的源代码("mipsel" 是 Little-Endian MIPS,与 MARS 使用的相同。)

然后我使用 llvm-objdump 转储 .data 部分(使用 "disassembly" 因为这是最简单的方法,尽管我不得不清理 b: 标签中不是以单词开头的重叠部分边界。)

$ llvm-objdump -D mips-align-clang-output.o         
00000000 a:
       0: 11 00                # manually cleaned up this line
00000002 b:
       2: 55 44 33 22                   addi    , , 17493

请注意 b 的地址为 2,而不是 4。 (这是一个未链接的 .o;当链接到可执行文件时,地址会更高。静态地用于位置相关的可执行文件,或者仅在 运行-time 用于 PIE)

在 GAS 语法中,.align 只是在那个位置 插入填充 ,直到到达对齐边界。所以你通常想把这样的指令 放在 标签之前,所以标签地址是对齐的并且在填充之后。也没有隐含的 .align 作为其他指令的一部分。

MARS(和老式 SGI)的行为对我来说听起来有点 "training wheels",但我想这在像 MIPS 这样的重度面向单词的 ISA 上是有一定意义的。这可以解释为什么我在 .asciz 后跟 .word 的 SO 上看到的一些代码在没有对齐错误的情况下工作 loads/stores 到这个词!尽管如此,让 assembler 为您计算字符串常量的长度仍然有缺点:


如果 MARS 的内置 assembler 甚至允许您执行 msg_len = msg_end - msg(例如从 .ascii 的结尾和开头减去标签,就像您在 GAS 或 NASM 语法),移动前面的标签可能会破坏字符串后面的 .word。 (通过将填充包含在字符串循环的长度计算中。)

但是 MARS 的 assembler 太糟糕了,无法让您在 assemble 时间计算大小,因此追溯移动较早的标签通常不是问题。我不确定经典 MIPS assemblers 是否允许您在 assemble 时间减去局部标签以获得恒定长度(例如 addiu $t0, $zero, end-start)。 MARS 没有,所以这个奇怪的(如果你习惯了现代 assemblers)"mis" 功能通常不会导致这个问题,除非你 la 开始和结束标签到寄存器用于具有 bne 循环条件的指针递增循环。

硬编码是愚蠢的,当 assembler 让你这样做时它很糟糕(通过不提供好的 label - label 功能。)

似乎 MARS 只是从 SGI 的 assembler(或者这个设计决定最初来自的任何地方)继承了这个错误特征。