Как метка в сборке может узнать свой адрес выполнения?
Я изучаю ассемблер (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.