Как метка в сборке может узнать свой адрес выполнения?

Я изучаю ассемблер (z80 и x86), и теперь я собираюсь разобраться с созданием двоичных файлов с использованием ассемблера.

Как можно использовать метки с абсолютными (в отличие от относительных) адресами?

Насколько я понимаю, ассемблер преобразует метку в адрес памяти при компиляции, но как ассемблеру узнать, какой адрес будет находиться во время выполнения метки?

Это кажется достаточно простым для системы z80 с открытым исходным кодом, поскольку вы можете загрузить программу по определенному адресу памяти и отправить RST сигнал, который укажет на счетчик программы 0000h, Что происходит, когда работает операционная система?

Не будет ли код, выполняемый в операционной системе, не знать его начальный адрес (и, следовательно, не сможет использовать не относительные коды операций, такие как call а также ret на этикетках)

4 ответа

Решение

Если машинный код не зависит от позиции, есть две общие стратегии:

  • включите в исполняемый файл дополнительную информацию (информацию о перемещении AKA), которая сообщит ОС, где должны быть настроены абсолютные адреса

  • просто загрузите исполняемый файл там, где он хочет, это означает, что вам, возможно, придется сначала выселить другого, ИЛИ вам нужно предоставить индивидуальные адресные пространства для каждого, так что в первую очередь не нужно бороться за нужное место

Возможно, стоит ознакомиться с решением CP/M, которое было просто: двоичный файл всегда загружается по фиксированному адресу, точка входа в ОС всегда находится по другому фиксированному адресу. Это было довольно типично для 8-битных машин, даже с формальными ОС, и было перенесено в MS-DOS. Это также технически возможно с многозадачными ОС, которые используют MMU, поскольку каждый процесс получает свое собственное адресное пространство, поэтому каждый двоичный файл может думать, что он был загружен в одном и том же месте.

Генерация между ними использует перемещаемый код. Либо он не зависит от местоположения, потому что процессор легко это поддерживает (в соответствии с классической Mac OS и адресацией 68000 по отношению к ПК), или, по сути, второй проход классического двухпроходного ассемблера происходит при загрузке двоичного файла. Таким образом, двоичный файл представляет собой скомпилированный код с заполнителями для всех абсолютных адресов и список мест размещения этих заполнителей, чтобы их можно было заменить после того, как фактические адреса станут известны.

Единственная проблема с этим заключается в том, что он препятствует быстрой виртуальной памяти. При подходе не-MMU для Mac OS программа компилируется в виде фрагментов по 16 КБ, причем переходы внутри каждого фрагмента происходят напрямую, а удаленные переходы проходят через пейджер. Если целевой блок загружен, он выключается, а если нет, то он загружается, и затем происходит скачок. Такая загрузка по требованию недопустима, если адреса должны быть рассчитаны и заполнены при каждой загрузке.

Думаю, ваша программа будет короче 64 кб. В этом случае программа должна знать только Смещение метки (известное как Near-Jump). Операционная система запускает программу каждый раз в одном и том же смещении, но в другом сегменте. условные переходы и "jmp short" используют только разницу между командой jmp и меткой. В некоторых особых случаях, например, если процедура сохраняется в стеке перед выполнением, компилятор вставляет код, который изменяет аргументы команды jmp.

Ассемблеры используют смещения.

LABEL
     . . . . . 

     JMP LABEL // Knows the number of bytes to label. SO label can be anywhere.
Другие вопросы по тегам