РЕДАКТИРОВАТЬ: в надежде сделать его более читабельным.
Аппаратное обеспечение не рассматривает память как длинный список неорганизованных байтов.Все процессоры с фиксированной или переменной длиной слова имеют определенный метод загрузки.Обычно известный адрес в памяти / адресном пространстве процессоров с адресом первой инструкции загрузочного кода или самой первой инструкции.Оттуда и для каждой инструкции адрес текущей инструкции находится там, где начинается декодирование.
Для x86, например, он должен смотреть на первый байт.В зависимости от декодирования этого байта может потребоваться прочитать больше байтов кода операции.Если инструкция требует адрес, смещение или другую форму непосредственного значения, эти байты также присутствуют.Очень быстро процессор точно знает, сколько байтов в этой инструкции.Если декодирование показывает, что инструкция содержит 5 байтов, и она начинается с адреса 0x10, то следующая инструкция имеет размер 0x10 + 5 или 0x15.Это продолжается вечно.Безусловные ветви, которые в зависимости от процессора могут иметь различные разновидности, вы не предполагаете, что байты, следующие за инструкцией, являются другой инструкцией.Ответвления, условные или безусловные, дают подсказку, где в памяти начинается другая инструкция или серия инструкций.
Обратите внимание, что сегодня X86 определенно не выбирает один байт за раз, когда он декодирует инструкцию, происходит чтение разумного размера,вероятно, 64 бита за раз, и процессор будет извлекать байты из этого по мере необходимости.При чтении одного байта из современного процессора шина памяти по-прежнему выполняет полноразмерное чтение и либо представляет все те биты на шине, где контроллер памяти извлекает только те биты, которые были после, либо может зайти так далеко, чтобы сохранить эти данные.,Вы увидите некоторые процессоры, в которых у вас могут быть две 32-битные инструкции чтения с двухадресных адресов, но только одно 64-битное чтение происходит на интерфейсе памяти.
Я настоятельно рекомендую вам написать дизассемблер и / или эмулятор.Для инструкций фиксированной длины это довольно просто, вы просто начинаете с начала и декодируете по мере прохождения памяти.Дизассемблер с фиксированной длиной слова может помочь узнать о командах декодирования, которые являются частью этого процесса, но это не поможет вам понять следование инструкциям с переменной длиной слова и разделить их без выравнивания.
MSP430хороший выбор в качестве первого дизассемблера.Есть инструменты GNU ASM и C, и т. Д. (И llvm в этом отношении).Начните с ассемблера, затем C или возьмите несколько готовых двоичных файлов.Ключ в том, что вы должны пройти код, как процессор, начать с вектора сброса и пройти свой путь.Когда вы декодируете одну инструкцию, вы знаете ее длину и знаете, где находится следующая инструкция, пока не дойдете до безусловной ветви.Если программист намеренно не оставил ловушку, чтобы обмануть дизассемблера, допустим, что все ветки условно или безусловно указывают на действительные инструкции.Полдень или вечер - это все, что требуется, чтобы разобраться в этом или хотя бы понять концепцию.Вам не обязательно нужно полностью декодировать инструкцию, не нужно превращать ее в полноценный дизассемблер, нужно только достаточно декодировать, чтобы определить длину инструкции и определить, является ли она ветвью, и если да, то где.Будучи 16-битной инструкцией, вы можете, если захотите, один раз создать таблицу всех возможных комбинаций битов инструкций и их длины, что может сэкономить некоторое время.Вы все еще должны расшифровать свой путь через ветви.
Некоторые люди могут использовать рекурсию, вместо этого я использую карту памяти, показывающую мне, какие байты являются началом инструкции, какие байты / слова являются частью инструкции, но непервый байт / слово и какие байты я еще не расшифровал.Я начинаю с взятия векторов прерывания и сброса и использую их для обозначения начальной точки для инструкций.а затем перейдите к циклу, который декодирует инструкции, ища больше отправных точек.Если проходитбез других отправных точек, то я закончил этот этап. Если в какой-то момент я нахожу начальную точку инструкции, находящуюся посередине инструкции, возникает проблема, для решения которой потребуется вмешательство человека. Например, разбирая старые диски с видеоиграми, вы, скорее всего, увидите это, написанный от руки ассемблер. Инструкции, сгенерированные компилятором
имеют тенденцию быть очень чистыми и предсказуемыми. Если я пройду через это с чистой картой памяти инструкций и того, что осталось, (предположим, данные), я могу сделать один проход, зная, где находятся инструкции, и декодировать и распечатать их. То, что дизассемблер для наборов команд переменной длины слова никогда не сможет сделать, - это найти каждую инструкцию. Если в наборе команд есть, например, таблица переходов или какой-либо иной вычисляемый адрес во время выполнения для выполнения, вы не найдете все из них без фактического выполнения кода.
Существует целый ряд существующих эмуляторов и дизассемблеров, и если вы хотите попытаться следовать им, а не писать свои собственные, у меня есть несколько http://github.com/dwelch67.
Есть плюсы и минусы за и против переменной и фиксированной длины слова. У Fixed есть свои преимущества: легко читается, легко декодируется, все красиво и правильно, но подумайте о ram, в частности, о кеше, вы можете собрать гораздо больше инструкций x86 в том же кеше, что и ARM. С другой стороны, ARM может декодировать намного проще, гораздо меньше логики, мощности и т. Д. Больше отдачи от затраченных средств. Исторически память была дорогой, логика была дорогой, и байт на ходу был таким, как он работал. однобайтовый код операции ограничил вас 256 командами, поэтому он расширился до некоторых кодов операций, требующих больше байтов, чтобы не упоминать непосредственные значения и адреса, которые в любом случае делали его переменной длины слова. Сохраняйте обратную совместимость десятилетиями, и вы окажетесь там, где вы сейчас находитесь.
Для добавления ко всей этой путанице, например, в ARM теперь есть набор команд переменной длины слова. У Thumb была одна переменная инструкция слова, ветвь, но вы можете легко декодировать ее как фиксированную длину. Но они создали thumb2, который действительно напоминает набор команд переменной длины слова. Также многие / большинство процессоров, которые поддерживают 32-битные архитектуры ARM, также поддерживают 16-битные инструкции большого пальца, поэтому даже с процессором ARM вы не можете просто выровнять данные по словам и декодировать по ходу работы, вы должны использовать переменную длину слова. Что еще хуже, переходы ARM в / из большого пальца декодируются путем выполнения, обычно вы не можете просто разобрать и определить руку от большого пальца. Ветвь, сгенерированная компилятором смешанного режима, часто включает в себя загрузку регистра с адресом ветвления, чтобы затем использовать инструкцию bx для ветвления к нему, поэтому дизассемблеру нужно будет посмотреть на bx, посмотреть назад в исполнении для регистра, используемого в ветке, и надеюсь, что вы найдете загрузку и надеетесь, что это сегмент .text, из которого он загружается.