Объявление указателя в спазме - PullRequest
0 голосов
/ 26 июня 2019

Я пишу простую ОС в Nasm, и я новичок в сборке.От c я привык иметь возможность объявлять указатели, не резервируя для них память и перемещая их, как мне нравится.Как я могу сделать это в спине?Если я объявляю переменную, например,

var: resb 1

, я понимаю, что я объявляю указатель и могу получить доступ к значению переменной, например,

mov eax, [var]

Хотя я не могу переместить указатель надругой адрес на

mov var, 0x1234

, тогда я получаю "недопустимую комбинацию кода операции и операндов".Так как я могу объявить и переместить указатели в памяти?

1 Ответ

2 голосов
/ 26 июня 2019

mov var, 0x1234 выдает ошибку, потому что var не является режимом адресации, [var] будет, но, тем не менее, вы все равно получите ошибку, поскольку NASM не может определить размер операции.

mov DWORD [var], 0x1234 будет делать (при условии, что ваша модель памяти использует 32-битные близкие указатели).

x86 не имеет косвенных перемещений адресов, в то время как в C вы можете использовать указатели "непосредственно из памяти"(грубо говоря), в сборке вы должны сначала загрузить указатель в регистр, а затем использовать этот регистр в качестве адреса.
Смотрите сами на Godbolt .

int* bar;

int foo()
{
    return *bar;
}

------

foo():                                # @foo()
        mov     eax, dword ptr [bar]
        mov     eax, dword ptr [eax]
        ret
bar:
        .long   0

Операция deference требует дополнительной инструкции, это может «выглядеть» как использование двух ссылок, но это не так (на самом деле в NASM можно видеть имя переменной как указатель на неено давайте все упростим).

Вы можете обращаться с указателем как с любой другой переменной (включая копировать ее), просто копируя ее в регистр и передавая ее (первая mov в приведенном выше примере делаетчто).
Например

mov eax, DWORD [var]
call foo                ;Call foo with the pointer in EAX

mov DWORD [var], 0x1234 ;Change the pointer value

Относительно резервирования памяти : любая переменная, когда хранится в памяти, занимает некоторое место, вот что делает программу состоянием.
resb используется для неинициализированных данных, здесь используются функции ELF для экономии места в скомпилированном двоичном файле на диске .
Если вы создаете свою собственную ОС, вы можетене использовать ELF вообще, поэтому resb (и аналогичные) могут просто откатиться при выделении инициализированной нулем переменной (NASM предупредит об этом).

Вы можете использовать стек для временного хранения ваших переменных, если они имеют ограниченную область действия;это будет использовать то же пространство там, ограничивая объем памяти.
В качестве альтернативы вы можете использовать %define для определения символов уровня сборки, они похожи (но не идентичны) на C #define s.
Например:

%define MY_PTR_TO_SOMETHING 0x1234

mov DWORD [MY_PTR_TO_SOMETHING], 1       ;In C this is *MY_PTR_TO_SOMETHING = 1;

Это не выделит место для MY_PTR_TO_SOMETHING, так как это просто псевдоним для номера 0x1234.Вы можете думать об этом как C #define MY_PTR_TO_SOMETHING ((int*)0x1234).Или как static const int *MY_PTR_TO_SOMETHING = (int*)0x1234; с компилятором, который оптимизирует фактическое статическое хранилище для самого объекта-указателя.

Обратите внимание, однако, что уровень косвенности пропал благодаря тому факту, что значение указателя теперь доступно какизвестная константа.
Конечно, вы все равно можете обойти это:

mov eax, MY_PTR_TO_SOMETHING         ;EAX = Holds the value of MY_PTR_TO_SOMETHING
mov ebx, DWORD [eax]                 ;EBX = Load a DWORD from the address 0x1234
;Store or copy the EAX register to pass the pointer around

Работа с указателями может сбивать с толку, если вы не используете режимы адресации x86.Мой совет: прочитайте инструкцию по эксплуатации для mov и убедитесь, что понимаете разницу между mov eax, 0x1234 и mov eax, DWORD [0x1234].


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

В C вы бы использовали int *ptr;, который зарезервировал бы место для объекта размером с указатель либо в статическом хранилище (в глобальной области видимости)или в регистре (или пространстве стека) для локальной области видимости.

...