MASM Offset vs. Label для адресации - PullRequest
0 голосов
/ 14 февраля 2020

В настоящее время я работаю над сборкой Irvine x86 Assembly, и я в четвертой главе.

Они ввели директиву OFFSET, но я не понимаю, почему я ' Я когда-либо использовал это. Почему бы мне просто не взять ярлык (который уже является адресом этих данных)? Кажется, OFFSET просто добавляет дополнительный шум.

У меня есть небольшая программа, чтобы проиллюстрировать мою точку зрения. У меня есть метка для некоторых данных с именем array, и я могу переместить элементы моего массива в al. Но в книге говорится об использовании директивы OFFSET, чтобы получить адрес array и переместить его в esi. Но это просто кажется мне ненужным, так как я мог бы просто использовать метку.

У меня есть два раздела кода, которые делают то же самое ниже. Один, где я использую метку для доступа к элементам массива, а другой - где OFFSET, чтобы переместить адрес в esi, а затем получить доступ к элементам массива.

.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode: DWORD

.data
    array   BYTE 10h, 20h, 30h, 40h, 50h

.code
main PROC
    xor eax, eax        ; set eax to 0

    ; Using Labels
    mov al, array      
    mov al, [array + 1]
    mov al, [array + 2]
    mov al, [array + 3]
    mov al, [array + 4]

    ; Using Offset
    mov esi, OFFSET array
    mov al, [esi]
    mov al, [esi + 1]
    mov al, [esi + 2]
    mov al, [esi + 3]
    mov al, [esi + 4]

    INVOKE ExitProcess, 0
main ENDP
END main

Действительно ли это всего лишь два способа достичь одного и того же?

Позже в книге, когда речь идет об указателях, у них есть такой пример:

.data
arrayB byte 10h, 20h, 30h, 40h
ptrB dword arrayB

И это имеет смысл для меня , ptrB содержит адрес arrayB. Но затем они говорят: «Опционально, вы можете delcare ptrB с оператором OFFSET, чтобы сделать отношения более ясными:«

ptrB dword OFFSET arrayB

Это не проясняет мне это в все. Я уже знаю arrayB это адрес. Похоже, что OFFSET просто брошено туда, и на самом деле он ничего не делает. Удаление OFFSET из этой последней строки в буквальном смысле приведет к тому же. Что именно делает OFFSET, если я могу в любом случае просто использовать метку для получения адреса?

1 Ответ

1 голос
/ 14 февраля 2020

Действительно ли это всего лишь два способа достичь одного и того же?

Да, у сборки есть множество способов сделать что-то.

C эквивалент будет
char *p = array;, затем с использованием p[0], p[1] et c. по сравнению с использованием array[0], array[1], et c.

Преимущество размещения указателя в регистре состоит в том, что он сохраняет некоторый размер кода при повторном его использовании; 2-байтовая инструкция mov с просто кодом операции + ModRM вместо кодирования абсолютного адреса в каждой инструкции отдельно для режима адресации [disp32].

Другое преимущество заключается в том, что вы можете увеличить указатель с inc esi. В других случаях, когда вы не развернули полностью oop, вам нужен либо указатель, либо индекс в регистре.

Простой указатель обычно лучше, чем [array + ecx], особенно лучше, чем [array + ecx*4] потому что индексированные режимы адресации имеют некоторые недостатки. ([array + ecx] технически не проиндексирован; он [base + disp32] и ему не требуется байт SIB, и он не считается индексированным для Режимы микросинтеза и адресации ).

You однако можно использовать смещения байтов (например, add ecx, TYPE array), чтобы разрешить режим адресации [base + disp32] в массиве stati c из int вместо [disp32 + idx*scale].

с использованием [disp32] каждый раз избегает необходимости в дополнительной инструкции для помещения адреса в регистр. mov reg, imm32 - это всего лишь 5-байтовая одиночная команда, но она все еще может не стоить производительности до того, как пара получит доступ к массиву c. Это может зависеть от того, как часто ваш код уже перегрет в кэше UOP, и от того, как часто он должен извлекать / декодировать. (Сохранение размера кода повышает частоту обращений L1 I $ или, по крайней мере, означает, что в одной строке кэша помещается больше инструкций, поэтому может быть целесообразно использовать больше инструкций / больше мопов, если он сохраняет размер кода в чем-то, что находится не в самой горячей внутренней области. oop.)

Перед тем, как al oop (не полностью развернутый), вам, как правило, потребуется инструкция обнуления счетчика / индекса al oop, например, xor ecx, ecx. Использование mov reg, imm32 только на 3 байта больше, и никаких дополнительных мопов. Если вы сохраняете 4 или 5 байтов каждый раз, когда используете указатель вместо режима индексированной адресации, вы уже опередили всего одну ссылку на массив за итерацию. И без дополнительных затрат. (Игнорирование любых незначительных различий между внешними затратами на выполнение инструкции xor-zeroing и mov-немедленного.)

Обратите внимание, что для x86-64 вы обычно ставите c адрес в регистре с 7-байтовым RIP-относительным LEA. А для того, чтобы ваш код был LargeAddressAware, вы не можете использовать [array + rcx], потому что он работает только с [disp32 + reg] режимом адресации, а не [RIP + rel32].


И, кстати, для согласованности I я рекомендую это более mov al, array

    mov al, [array + 0]
    mov al, [array + 1]
    ...

Первый комментарий по вашему вопросу от кого-то, кого вы запутали, выполнив mov al, array, а затем mov al, [array + 1], используя 2 разных синтаксиса для похожих адресов; Я думаю, что Шут думал, что вы намеревались что-то вроде mov al, OFFSET array. Кстати, вместо этого вы могли бы написать это так (я думаю)

mov al, array
mov al, array + 1

, но я всегда рекомендую использовать квадратные скобки вокруг операнда памяти для ясности. Особенно, если вы когда-либо смотрите на синтаксис NASM где это всегда обязательно, но некоторые люди рекомендуют это соглашение, даже если вы используете только MASM. (Но имейте в виду, что MASM игнорирует скобки в некоторых случаях, когда нет регистра: Запутанные скобки в MASM32 , поэтому не думайте, что использование скобок в MASM делает его работающим как NASM.)


Кстати, эффективный способ загрузки одного байта заключается в расширении нуля до полного регистра, вместо слияния с младшим байтом полного регистра. movzx eax, byte ptr [esi]


Кстати, да, mov esi, OFFSET array (5 байт) - наиболее эффективный способ помещения адреса stati c в регистр ( размер и производительность кода). lea esi, array имеет размер 6 байт (код операции + modrm + [disp32] режим адресации) и может работать на меньшем количестве исполнительных портов; никогда не используйте LEA без регистра в 32-битном режиме.

В 64-битном режиме вы хотите lea rsi, array, потому что MASM автоматически использует RIP-относительную адресацию для того, что вы хотите. В противном случае по-прежнему используйте mov esi, OFFSET array (да, ESI, а не RSI) для кода, который не является LargeAddressAware и может по-прежнему использовать компактный код с использованием 32-разрядных абсолютных адресов.

...