TL:DR:MARS 工具提示具有误导性;您需要使用以下命令禁用该部分其余部分的自动对齐.align 0
。你不能just下一个单词对齐不足。
.align 1
确实按2对齐,这不是问题。例如尝试一下之间.byte
or .ascii
伪指令。
例如该源生成 0x00110062 作为 .data 部分的第一个字,就像.byte 'b', 0, 0x11, 0
would.
.data
a: .ascii "b"
b:
.align 1
.byte 0x11
And the b:
标签有地址2
, after对齐填充。
(我将 MARS 设置为“紧凑”内存布局,数据部分从地址开始0
为了简单起见。)
到目前为止我们所看到的确实与您为其 Unix 汇编器链接的 Silicon Graphics 文档相匹配。 (这与 GNU 等现代汇编器的方式非常不同as
(又名 GAS)和 clang 工作。)
SGI 文档说:
前进位置计数器以使表达式低阶
计数器零的位。通常情况下,.half
, .word
, .float
, and
.double
指令会自动适当地对齐其数据。为了
例子,.word
执行隐式 .align 2 (.double
执行 .align 3)。您可以使用以下命令禁用自动对齐功能.align 0
。这
汇编器在下一次恢复自动对齐.text
, .data
,
.rdata
, or .sdata
指示。
紧邻自动或显式对齐之前的标签
也进行了重新调整。例如,foo: .align 3; .word 0
是一样的
作为.align 3; foo: .word0
.
这并没有说明有关使用的任何内容.align 1
to under- 对齐下一个.word
。只是您可以完全关闭隐式对齐作为数据指令的一部分.align 0
。拥有.align 1
覆盖并欠对齐下一个.word
不必禁用自动对齐本来是有意义的并且是一个有效的设计,但这不是他们选择实现的功能。
(注意.align 0
很特别:按 1 字节对齐永远不需要插入任何填充;当前位置始终是字节边界。因为没有理由使用.align 0
为了对齐单个位置,语法的设计者可以用不同的含义重载它:禁用自动对齐。)
MARS 确实支持这一点。 (And then .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
) 不只是在当前位置插入填充,它还插入它before任何前面的标签,就在最后一条数据之后。
您当然可以使用发出任何您想要的数据.byte
or .half
如果您确实想避免隐式对齐 4 字节边界而不禁用自动对齐,请使用指令。您通常实际上并不想要这样,并且在大多数情况下它可以帮助初学者避免遇到对齐问题。 MIPS 是一种高度面向字的 ISA,因此通常没有理由出现未对齐的情况.word
.
我看到的唯一 MARS 错误是可用性:一个非常具有误导性的工具提示。
目前它说在指定字节边界上对齐下一个数据项:(0=字节、1=半、2=字、3=双)。这似乎意味着您可以未对齐.word
。这是非常具有误导性的.align 0
这实际上禁用了该部分其余部分的自动对齐。
这不是如何.align
适用于使用 GAS 语法(GNUas
或叮当声)。(例如,参见气体手册)
在我的 Linux 桌面上,我使用以下命令组装了源代码clang -c -target mipsel mips-align.s
(“mipsel”是 Little-Endian MIPS,与 MARS 使用的相同。)
然后我使用 llvm-objdump 转储 .data 部分(使用“反汇编”,因为这是最简单的方法,尽管我必须清理不从字边界开始的 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 $19, $17, 17493
注意b
有地址2
, not 4
。 (这是一个未链接的.o
;当链接到可执行文件时,地址会更高。静态地用于位置相关的可执行文件,或者仅在运行时的 PIE)
在 GAS 语法中,.align
只需插入填充在那个位置直到到达对齐边界。所以你通常想要放置这样的指令before标签,因此标签地址对齐并位于填充之后。也没有隐含的.align
作为其他指令的一部分。
MARS(和老式 SGI)的行为对我来说听起来有点像“训练轮”,但我想这对于像 MIPS 这样的严重面向字的 ISA 来说是有意义的。这可以解释为什么我在 SO 上看到过一些代码.asciz
其次是.word
加载/存储到单词时不会出现对齐错误!不过,让汇编器为您计算字符串常量的长度也有缺点:
如果 MARS 的内置汇编器允许你这样做msg_len = msg_end - msg
(从末尾和开头减去标签.ascii
例如,就像在 GAS 或 NASM 语法中一样),移动前面的标签可能会破坏.word
在一个字符串之后。 (通过在字符串循环的长度计算中包含填充。)
但是 MARS 的汇编器太糟糕了,无法让您在汇编时计算尺寸,因此追溯移动较早的标签通常不是问题。我不确定经典的 MIPS 汇编器是否允许您在汇编时减去本地标签以获得恒定的长度(例如addiu $t0, $zero, end-start
) 或不。 MARS 不会,所以这个奇怪的(如果你习惯了现代汇编器)“错误”功能通常不会导致这个问题,除非你la
将开始和结束标签放入寄存器中,以便在指针增量循环中使用bne
循环条件。
硬编码是愚蠢的,当汇编器让你这样做时(通过不提供良好的label - label
特征。)
看起来 MARS 只是从 SGI 的汇编器(或者这个设计决策最初来自的地方)继承了这个错误特征。