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