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 установлен на «компактную» схему памяти, раздел данных начинается с адреса 0
для простоты.)
То, что мы видим до сих пор, соответствует документации Silicon Graphics, которую вы связали для их Unix ассемблера. (Это сильно отличается от того, как работают современные ассемблеры, такие как GNU as
(также известный как GAS) и clang.)
В документации SGI говорится:
Чтобы увеличить счетчик местоположения, добавьте Выражение младших битов счетчика ноль. Обычно директивы .half
, .word
, .float
и .double
автоматически выравнивают свои данные соответствующим образом. Например, .word
делает неявное .align 2 (.double
делает .align 3). Вы отключите автоматическое выравнивание c с помощью .align 0
. Ассемблер восстанавливает автоматическое выравнивание c в следующей директиве .text
, .data
, .rdata
или .sdata
.
Метки, непосредственно предшествующие автомату c, или явное выравнивание также перестраиваются. Например, foo: .align 3; .word 0
совпадает с .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
) не просто вставляет заполнение в текущей позиции, оно вставляет перед любыми предыдущими метками, сразу после последнего фрагмента данных.
You может, конечно, излучать любые данные, которые вы хотите использовать директивами .byte
или .half
, если вы действительно хотите избежать неявного выравнивания по 4-байтовым границам, не отключая автоматическое выравнивание. Обычно вы этого не хотите, и это избавит новичков от проблем с выравниванием в большинстве случаев. MIPS - это сильно ориентированный на слова ISA, поэтому, как правило, нет особых оснований для подчеркивания .word
.
Единственная ошибка MARS, которую я вижу, это удобство использования: очень вводящая в заблуждение подсказка.
It в настоящее время говорит выровнять следующий элемент данных по указанной границе байта: (0 = байт, 1 = половина, 2 = слово, 3 = двойная) . Похоже, это подразумевает, что вы могли бы выровнять .word
. И это очень вводит в заблуждение о .align 0
, который фактически отключает автоматическое выравнивание для остальной части раздела.
Это не то, как .align
работает в ассемблерах, которые используют синтаксис GAS (GNU *) 1104 * или лязг). (например, см. руководство по ГАЗУ )
На моем рабочем столе Linux я собрал ваш исходный код, используя clang -c -target mipsel mips-align.s
("mipsel" - Little -Endian MIPS, так же, как использует MARS.)
Затем я использовал llvm-objdump, чтобы выгрузить. раздел данных (с «разборкой», потому что это самый простой способ, хотя мне пришлось убирать перекрытие с меткой 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
, а не 4
. (Это несвязанный .o
; при связывании с исполняемым файлом адрес будет выше. Статически для позиционно-зависимого исполняемого файла или только во время выполнения для P IE)
In Синтаксис GAS, .align
просто вставляет отступ в эту позицию , пока не достигнет границы выравнивания. Поэтому обычно вы хотите поместить такие директивы перед метками, чтобы адрес метки был выровнен и появился после заполнения. Также нет неявного .align
как части других директив.
Поведение MARS (и старой школы SGI) звучит для меня своего рода «тренировочными кругами», но я думаю, что это имеет смысл в значительном слове - ориентированный на ISA, как MIPS. Это объясняет, почему какой-то код, который я видел в SO с .asciz
, за которым следует .word
, работает без ошибок выравнивания при загрузке / сохранении слова! Тем не менее, у него есть недостатки, позволяющие ассемблеру вычислять для вас длину строковой константы:
Если встроенный ассемблер MARS даже позволяет вам сделать msg_len = msg_end - msg
(вычитая метки из конца и начала например, .ascii
, как в синтаксисе GAS или NASM), перемещение предшествующих меток может нарушить это для .word
после строки. (Включая заполнение в вычисление длины для al oop над строкой.)
Но ассемблер MARS слишком много отстает, чтобы позволить вам вычислить размеры во время сборки, поэтому задним числом перемещение более ранних меток обычно не является проблемой , Я не уверен, позволяют ли ассемблеры classi c MIPS вычитать локальные метки во время сборки, чтобы получить постоянную длину (например, addiu $t0, $zero, end-start
) или нет. MARS не делает, так что эта странная (если вы привыкли к современным ассемблерам) функция «mis» обычно не вызывает этой проблемы, если только вы не la
начинаете и заканчиваете метки в регистрах для использования с приращением указателя l oop с условием bne
l oop.
Жесткое кодирование является глупым, и оно плохо, когда ассемблер заставляет вас сделать это (не предоставляя хороших label - label
функций.)
Похоже, что MARS только что унаследовал эту ошибку от ассемблера SGI (или от того, откуда изначально пришло это дизайнерское решение).