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(或者这个设计决定最初来自的任何地方)继承了这个错误特征。
我有以下数据段
.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(或者这个设计决定最初来自的任何地方)继承了这个错误特征。