учебники по первому проходу и второму проходу ассемблера - PullRequest
2 голосов
/ 09 февраля 2012

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

Ответы [ 2 ]

3 голосов
/ 09 февраля 2012

Не знаю ни одного учебника, на самом деле ничего особенного.

one:
  inc r0
  cmp r0,0
  jnz one
  call fun
  add r0,7
  jmp more_fun
fun:
  mov r1,r0
  ret
more_fun:

Ассемблер / программное обеспечение, подобно человеку, будет читать исходный файл сверху вниз, байт 0 в файле до конца. не существует жестких и быстрых правил относительно того, что вы выполняете в каждом проходе, и это не обязательно проход «по файлу», но проход «по данным».

Первый проход: Когда вы читаете каждую строку, вы анализируете ее. Вы строите некую структуру данных, в которой есть инструкции в порядке файлов. Когда вы сталкиваетесь с меткой вроде::, вы отслеживаете, перед какой инструкцией стояли инструкции, или, возможно, у вас есть маркер между инструкциями, как бы вы ни хотели его реализовать. Когда вы сталкиваетесь с инструкцией, в которой используется метка, у вас есть два варианта выбора, вы можете прямо сейчас поискать эту метку, и если это метка, обращенная назад, то вы должны были увидеть ее уже как инструкцию jnz one. ЕСЛИ вы до сих пор отслеживали количество и размер (если переменная длина слова) инструкции, которые вы можете выбрать для кодирования этой инструкции сейчас, если это относительная инструкция, если набор инструкций использует абсолютную, вы могли бы иметь в любом случае просто оставить заполнитель.

Теперь инструкции fun fun и jump more_fun создают проблему: когда вы получаете эти инструкции, вы не можете разрешить их в настоящее время, вы не знаете, являются ли эти метки локальными для этого файла или находятся в другом файле, поэтому вы не можете кодировать Эта инструкция на первом проходе, вы должны сохранить ее на потом, и это причина для второго прохода.

Второй проход, скорее всего, будет проходом по вашим структурам данных, а не по файлу, и это сильно зависит от реализации. Например, у вас может быть одномерный массив структур, и все там. Например, вы можете сделать много проходов по этим данным, запустить один индекс через массив в поисках неразрешенных меток. Когда вы найдете неразрешенную метку, отправьте второй индекс через массив в поисках определения метки. Если вы не нашли его, то, в зависимости от приложения, ваш ассемблер создает объекты, которые будут связаны позже, или он создает двоичный файл, нужно ли разрешить все в этой одной сборке до двоичного шага? Если объект, то вы предполагаете, что это внешний, если только для конкретного приложения ваш ассемблер не требует, чтобы внешние метки были определены как внешние. То, является ли отсутствующая метка ошибкой, зависит от конкретного приложения. если это не ошибка, то, в зависимости от приложения, вы должны кодировать для самого длинного / самого дальнего типа ветви, оставляя для адреса компоновщик подробные данные.

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

top:
  ...
  jmp down
  ...
  jnz top
  ...
down:

Когда вы кодируете jmp вниз, вы можете выбрать оптимистично, чтобы кодировать его как меньшую (число байтов / слов, если переменная длина слова) относительную ветвь, оставляя расстояние, которое будет определено. Когда вы доберетесь до вершины jnz, допустим, что он находится точно в байте, достаточно близком к вершине, чтобы кодировать с использованием относительной ветви. На втором проходе, хотя вы должны вернуться и закончить jmp, вы обнаружите, что он не достигнет, вам нужно больше байтов / слов, чтобы закодировать его как длинную ветвь. Теперь вершина jnz также должна стать дальней ветвью (вызывая повторное движение вниз). Вы должны продолжать пропускать инструкции, вычисляя их расстояние далеко / коротко, пока не сделаете проход без изменений. Будьте осторожны, чтобы не оказаться в бесконечном цикле, где за один проход вы укорачиваете инструкцию, но это заставляет другого удлиняться, а на следующем проходе удлинение одного заставляет другого удлиняться, а второго сокращать, и это повторяется навсегда .

Мы могли бы вернуться к началу этого, и на вашем первом проходе вы могли бы построить более одной или нескольких структур данных, возможно, когда вы идете, вы создадите список найденных меток и список отсутствующих меток. И на втором проходе вы просматриваете список пропавших без вести и видите, находятся ли они в найденных, а затем решаете их таким образом. Или, может быть, на первом проходе, и некоторые могут утверждать, что это однопроходный ассемблер, когда вы находите метку, прежде чем продолжить просмотр файла, вы оглядываетесь назад, чтобы увидеть, искал ли кто-нибудь эту метку (или эта метка уже была определена). чтобы объявить ошибку) Я бы назвал это многопроходным ассемблером, потому что он все еще проходит через данные много раз.

А теперь давайте сделаем все намного хуже. Посмотрите на набор инструкций arm в качестве примера и любой другой набор инструкций фиксированной длины. Ваши относительные ветви обычно закодированы в одной инструкции, таким образом, набор команд фиксированной длины. Дальняя ветвь обычно включает в себя загрузочный ПК из данных, найденных по этому адресу, а это означает, что вам действительно нужно два элемента инструкции, а затем где-то в относительной области действия этой команды слово данных, содержащее абсолютный адрес места для ветвления. Вы можете заставить пользователя создавать их, но, например, с помощью ассемблеров ARM они могут сделать это за вас, простейший пример:

ldr r0,=0x12345678
...
b somewhere

Этот синтаксис означает загрузку r0 со значением 0x12345678, которая не вписывается в инструкцию постановки на охрану. То, что ассемблер делает с этим синтаксисом, так это то, что он пытается найти мертвую точку в коде в пределах досягаемости этой инструкции, где он может поместить значение данных, затем он кодирует эту инструкцию как загрузку с относительного адреса ПК. Например, после безусловной ветки это хорошее место для сокрытия данных. иногда вы должны использовать директивы, такие как .pool, чтобы поощрять или напоминать ассемблеру хорошие места для вставки этих данных. r0 - это не программный счетчик, а r15, и вы можете использовать r15 там, чтобы связать это с обсуждением ветвления выше.

Взгляните на ассемблер, который я создал для этого проекта http://github.com/dwelch67/lsasim, набор инструкций фиксированной длины, но я заставляю пользователя выделять слово и загружать из него, я не разрешаю ярлык, к которому склонны ассемблеры позволяют.

Надеюсь, это поможет объяснить вещи. Суть в том, что вы не можете разрешить метки за один линейный проход через данные, вы должны вернуться назад и соединить точки с метками с прямой ссылкой. И я утверждаю, что вам все равно нужно сделать много проходов, чтобы разрешить все длинные / короткие кодировки (если только набор / синтаксис инструкций не заставляет пользователя явно указывать абсолютную или относительную ветвь, а некоторые выполняют rjmp против jmp или rjmp против ljmp, rcall против звонка и т. д.). Сделать один проход в «файл» наверняка, не проблема. Если вы разрешите директивы типа include, некоторые инструменты создадут временный файл, в который он извлекает все включения при создании одного файла, в котором нет включений, а затем инструмент делает один проход по этому (например, gcc управляет включениями, например, сохраните промежуточные файлы когда-нибудь и посмотрите, какие файлы создаются) (если вы сообщаете номера строк с предупреждениями / ошибками, вам нужно управлять строками временных файлов по сравнению с исходным именем и строкой файла.).

2 голосов
/ 09 февраля 2012

Хорошее начало - книга Дэвида Соломона, Сборщики и загрузчики . Это старая книга, но информация все еще актуальна.

Вы можете скачать PDF книги .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...