Указатели сборки x86

Надеюсь, это не глупый вопрос, но я пытаюсь сосредоточиться на указателях в сборке.

В чем именно разница между:

mov eax, ebx

а также

mov [eax], ebx

и когда должен dword ptr [eax] должен быть использован?

Также, когда я пытаюсь сделать mov eax, [ebx] Я получаю ошибку компиляции, почему это?

1 ответ

Решение

Как уже было сказано, заключение в скобки вокруг операнда означает, что этот операнд должен быть разыменован, как если бы он был указателем в C. Другими словами, скобки означают, что вы читаете значение из (или сохраняете значение в) это место в памяти, а не чтение этого значения напрямую.

Итак, это:

mov  eax, ebx

просто копирует значение в ebx в eax, В псевдо-C нотации это будет: eax = ebx,

Тогда как это:

mov  eax, [ebx]

разыменовывает содержимое ebx и сохраняет указанную стоимость в eax, В псевдо-C нотации это будет: eax = *ebx,

Наконец, это:

mov  [eax], ebx

сохраняет значение в ebx в ячейку памяти, на которую указывает eax, Опять же, в псевдо-C нотации: *eax = ebx,


Регистры здесь также могут быть заменены операндами памяти, такими как имена символических переменных. Итак, это:

mov  eax, [myVar]

разыменовывает адрес переменной myVar и хранит содержимое этой переменной в eax, лайк eax = myVar,

В отличие от этого:

mov  eax, myVar

хранит адрес переменной myVar в eax, лайк eax = &myVar,

По крайней мере, так работает большинство ассемблеров. Ассемблер Microsoft (называемый MASM) и встроенная сборка компилятора Microsoft C/C++ немного отличаются. Он рассматривает две вышеупомянутые инструкции как эквивалентные, по существу игнорируя скобки вокруг операндов памяти.

Чтобы получить адрес переменной в MASM, вы должны использовать OFFSET ключевое слово:

mov  eax, OFFSET myVar

Однако, даже несмотря на то, что MASM имеет этот прощающий синтаксис и позволяет вам быть небрежным, вы не должны этого делать. Всегда включайте скобки, когда вы хотите разыменовать переменную и получить ее фактическое значение. Вы никогда не получите неправильный результат, если напишите код явно, используя правильный синтаксис, и это облегчит другим понимание. Кроме того, это заставит вас привыкнуть писать код так, как его ожидают другие ассемблеры, а не полагаться на костыль MASM "делай то, что я имею в виду, а не то, что я пишу".

Говоря о том, что костыль "делай то, что я имею в виду, а не то, что я пишу", MASM также обычно позволяет вам избежать опускания спецификатора размера операнда, так как он знает размер переменной. Но опять же, я рекомендую написать это для ясности и последовательности. Следовательно, если myVar является int, вы бы сделали:

mov  eax, DWORD PTR [myVar]    ; eax = myVar

или же

mov  DWORD PTR [myVar], eax    ; myVar = eax

Эта нотация необходима в других ассемблерах, таких как NASM, которые не являются строго типизированными и не помнят этого myVar это DWORD размер памяти.

Это вообще не нужно при разыменовании регистровых операндов, так как имя регистра указывает на его размер. al а также ah всегда BYTE -sized, ax всегда WORD -sized, eax всегда DWORD и rax всегда QWORD -sized. Но в любом случае не мешало бы включить его, если хотите, для согласованности с тем, как вы записываете операнды в памяти.


Также, когда я пытаюсь сделать mov eax, [ebx] Я получаю ошибку компиляции, почему это?

Хм... ты не должен. Это прекрасно для меня в сборке MSVC. Как мы уже видели, это эквивалентно:

mov  eax, DWORD PTR [ebx]

и означает, что область памяти, указанная ebx будет разыменовано и что DWORD значение будет загружено в eax,


почему я не могу сделать mov a, [eax] Разве это не делает "a" указателем на то, куда указывает eax?

Нет. Эта комбинация операндов не допускается. Как видно из документации к MOV Инструкция, по сути, существует пять возможностей (игнорирование альтернативных кодировок и сегментов):

mov  register, register     ; copy one register to another
mov  register, memory       ; load value from memory into register
mov  memory,   register     ; store value from register into memory
mov  register, immediate    ; move immediate value (constant) into register
mov  memory,   immediate    ; store immediate value (constant) in memory

Обратите внимание, что нет mov memory, memory, что вы пытались.

Тем не менее, вы можете сделать a указать на то, что eax указывает на простое кодирование:

mov  DWORD PTR [a], eax

Сейчас a а также eax имеют одинаковое значение. Если eax был указатель, то a теперь указатель на ту же самую область памяти.

Если вы хотите установить a к значению, которое eax указывает на то, что вам нужно будет сделать:

mov  eax, DWORD PTR [eax]    ; eax = *eax
mov  DWORD PTR [a], eax      ; a   = eax

Разумеется, это затирает указатель и заменяет его разыменованным значением. Если вы не хотите потерять указатель, вам придется использовать второй регистр "нуля"; что-то вроде:

mov  edx, DWORD PTR [eax]    ; edx = *eax
mov  DWORD PTR [a], edx      ; a   = edx

Я понимаю, что все это несколько сбивает с толку. mov Инструкция перегружена большим количеством потенциальных значений в x86 ISA. Это связано с корнями x86 как архитектуры CISC. Современные RISC-архитектуры, напротив, лучше справляются с разделением перемещений регистров-регистров, загрузок памяти и хранилищ памяти. х86 впихивает их всех в один mov инструкция. Слишком поздно, чтобы вернуться и исправить это сейчас; Вы просто должны освоиться с синтаксисом, и иногда это занимает второй взгляд.

Другие вопросы по тегам