stringtranslate.com

Машинный код

Монитор машинного языка, работающий на микропроцессоре W65C816S , отображающий дизассемблированный код и дампы регистра и памяти процессора

В программировании машинный код — это компьютерный код , состоящий из инструкций машинного языка , которые используются для управления центральным процессором компьютера (ЦП). Для обычных двоичных компьютеров машинный код — это двоичное представление компьютерной программы, которое фактически считывается и интерпретируется компьютером. Программа в машинном коде состоит из последовательности машинных инструкций (возможно, перемежаемых данными). [1]

Каждая инструкция машинного кода заставляет ЦП выполнять определенную задачу. Примеры таких задач включают:

  1. Загрузить слово из памяти в регистр ЦП
  2. Выполнить операцию арифметико-логического устройства (АЛУ) над одним или несколькими регистрами или ячейками памяти
  3. Перейти или пропустить инструкцию, которая не является следующей

В целом, каждое семейство архитектур (например, x86 , ARM ) имеет собственную архитектуру набора инструкций (ISA) и, следовательно, свой собственный язык машинного кода. Существуют исключения, такие как архитектура VAX , которая включает дополнительную поддержку набора инструкций PDP-11 ; архитектура IA-64 , которая включает дополнительную поддержку набора инструкций IA-32 ; и микропроцессор PowerPC 615 , который может изначально обрабатывать как наборы инструкций PowerPC , так и x86.

Машинный код — это строго числовой язык, и это интерфейс самого низкого уровня к ЦП, предназначенный для программиста. Язык ассемблера  обеспечивает прямое отображение между числовым машинным кодом и понятным человеку мнемоническим кодом. В ассемблере числовые коды операций и операнды заменяются мнемоническими кодами и метками. Например, архитектура x86 имеет доступный код операции 0x90; он представлен как NOP в исходном коде ассемблера . Хотя можно писать программы непосредственно в машинном коде, управление отдельными битами и вычисление числовых адресов утомительно и подвержено ошибкам. Поэтому программы редко пишутся непосредственно в машинном коде. Однако существующую программу в машинном коде можно редактировать, если исходный код ассемблера недоступен.

Большинство программ сегодня написаны на языке высокого уровня . Программа высокого уровня может быть переведена в машинный код компилятором .

Набор инструкций

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

Набор инструкций процессора может иметь инструкции фиксированной или переменной длины. То, как организованы шаблоны, зависит от конкретной архитектуры и типа инструкции. Большинство инструкций имеют одно или несколько полей кода операции , которые определяют базовый тип инструкции (например, арифметический, логический, переход и т. д.), операцию (например, сложение или сравнение) и другие поля, которые могут давать тип операнда ( ов), режим(ы) адресации , смещение(я) адресации или индекс или само значение операнда (такие постоянные операнды, содержащиеся в инструкции, называются немедленными ). [2]

Не все машины или отдельные инструкции имеют явные операнды. На машине с одним аккумулятором аккумулятор неявно является как левым операндом, так и результатом большинства арифметических инструкций. Некоторые другие архитектуры, такие как архитектура x86 , имеют версии аккумуляторов общих инструкций, при этом аккумулятор рассматривается как один из общих регистров более длинными инструкциями. Стековая машина имеет большинство или все свои операнды в неявном стеке. Инструкции специального назначения также часто не имеют явных операндов; например, CPUID в архитектуре x86 записывает значения в четыре неявных регистра назначения. Это различие между явными и неявными операндами важно в генераторах кода, особенно в частях распределения регистров и отслеживания диапазона в реальном времени. Хороший оптимизатор кода может отслеживать неявные и явные операнды, что может позволить более частое распространение констант , свертывание констант регистров (регистр, назначенный результат константного выражения, освобожденный путем замены его этой константой) и другие улучшения кода.

Языки ассемблера

Перевод машинного языка на язык ассемблера

Гораздо более удобная для человека версия машинного языка, называемая языком ассемблера , использует мнемонические коды для обозначения инструкций машинного кода, а не непосредственно числовые значения инструкций, а также использует символические имена для обозначения мест хранения и иногда регистров . [3] Например, на процессоре Zilog Z80 машинный код 00000101, который заставляет ЦП уменьшать B регистр общего назначения , будет представлен на языке ассемблера как DEC B. [4]

Примеры

IBM709x

IBM 704, 709, 704x и 709x хранят одну инструкцию в каждом командном слове; IBM нумерует бит слева направо как S, 1, ..., 35. Большинство инструкций имеют один из двух форматов:

Общий
С,1-11
12-13 Флаг, игнорируется в некоторых инструкциях
14-17 неиспользованные
18-20 Тег
21-35 лет
Управление индексным регистром, отличное от TSX
S,1-2 Код операции
3-17 Уменьшение
18-20 Тег
21-35 лет

Для всех, кроме IBM 7094 и 7094 II, есть три индексных регистра, обозначенных как A, B и C; индексация с несколькими битами 1 в теге вычитает логическое ИЛИ выбранных индексных регистров, а загрузка с несколькими битами 1 в теге загружает все выбранные индексные регистры. 7094 и 7094 II имеют семь индексных регистров, но при включении они находятся в режиме множественных тегов , в котором они используют только три индексных регистра способом, совместимым с более ранними машинами, и требуют инструкции Leave Multiple Tag Mode ( LMTM ) для доступа к остальным четырем индексным регистрам.

Эффективный адрес обычно YC(T), где C(T) — это либо 0 для тега 0, либо логический или выбранных индексных регистров в режиме множественных тегов или выбранный индексный регистр, если не в режиме множественных тегов. Однако эффективный адрес для инструкций управления индексным регистром — это просто Y.

Флаг с обоими битами 1 выбирает косвенную адресацию; слово косвенного адреса имеет как тег, так и поле Y.

Помимо инструкций переноса (перехода), эти машины имеют инструкции пропуска, которые условно пропускают одно или два слова, например, сравнение аккумулятора с хранилищем (CAS) выполняет трехстороннее сравнение и условно пропускает NSI, NSI+1 или NSI+2 в зависимости от результата.

MIPS

Архитектура MIPS представляет собой конкретный пример машинного кода, инструкции которого всегда имеют длину 32 бита. [5] : 299  Общий тип инструкции задается полем op (операция), старшими 6 битами. Инструкции J-типа (переход) и I-типа (немедленный) полностью определяются op . Инструкции R-типа (регистр) включают дополнительное поле funct для определения точной операции. Поля, используемые в этих типах:

 6 5 5 5 5 6 бит[ op | rs | rt | rd |shamt| funct] R-тип[ op | rs | rt | адрес/немедленный] I-тип[ op | целевой адрес ] J-тип

rs , rt и rd обозначают операнды регистра; shamt задает величину сдвига; а поля адреса или непосредственного адреса содержат непосредственно операнд. [5] : 299–301 

Например, сложение регистров 1 и 2 и помещение результата в регистр 6 кодируется: [5] : 554 

[ оп | рс | рт | рд | шамт | фунц] 0 1 2 6 0 32 десятичный 000000 00001 00010 00110 00000 100000 двоичный

Загрузите значение в регистр 8, взятое из ячейки памяти, расположенной на 68 ячеек после ячейки, указанной в регистре 3: [5] : 552 

[ op | rs | rt | адрес/немедленно] 35 3 8 68 десятичная 100011 00011 01000 00000 00001 000100 двоичный

Переходим по адресу 1024: [5] : 552 

[ оп | целевой адрес ] 2 1024 десятичная 000010 00000 00000 00000 10000 000000 двоичный

Перекрывающиеся инструкции

На архитектурах процессоров с наборами инструкций переменной длины [6] (таких как семейство процессоров Intel x86 ) в пределах феномена повторной синхронизации потока управления, известного как счет Крускала , [7] [6] [8] [9] [10] иногда возможно посредством программирования на уровне опкода намеренно организовать результирующий код таким образом, чтобы два пути кода совместно использовали общий фрагмент последовательностей опкодов. [nb 1] Это называется перекрывающимися инструкциями , перекрывающимися опкодами , перекрывающимся кодом , перекрывающимся кодом , разрывом инструкции или переходом в середину инструкции . [11] [12] [13]

В 1970-х и 1980-х годах перекрывающиеся инструкции иногда использовались для сохранения пространства памяти. Одним из примеров была реализация таблиц ошибок в Altair BASIC от Microsoft , где чередующиеся инструкции взаимно делили свои байты инструкций. [14] [6] [11] Сегодня эта техника используется редко, но к ней все еще может потребоваться прибегнуть в областях, где необходима экстремальная оптимизация размера на уровне байтов, например, при реализации загрузчиков , которые должны вписываться в загрузочные секторы . [nb 2]

Иногда его также используют как метод запутывания кода , как меру против разборки и подделки. [6] [9]

Этот принцип также используется в общих последовательностях кода толстых двоичных файлов , которые должны работать на нескольких процессорных платформах, несовместимых по набору инструкций. [примечание 1]

Это свойство также используется для поиска непреднамеренных инструкций, называемых гаджетами, в существующих репозиториях кода и используется в возвратно-ориентированном программировании как альтернатива внедрению кода для таких эксплойтов, как атаки возврата в libc . [15] [6]

Связь с микрокодом

В некоторых компьютерах машинный код архитектуры реализуется еще более фундаментальным базовым слоем, называемым микрокодом , который обеспечивает общий интерфейс машинного языка для линейки или семейства различных моделей компьютеров с существенно различающимися базовыми потоками данных . Это делается для облегчения переноса программ машинного языка между различными моделями. Примером такого использования является семейство компьютеров IBM System/360 и их последователи.

Связь с байт-кодом

Машинный код, как правило, отличается от байт-кода (также известного как p-код), который либо выполняется интерпретатором, либо сам компилируется в машинный код для более быстрого (прямого) выполнения. Исключением является случай, когда процессор предназначен для использования определенного байт-кода непосредственно в качестве своего машинного кода, как в случае с процессорами Java .

Машинный код и ассемблерный код иногда называют собственным кодом , когда речь идет о платформенно-зависимых частях языковых функций или библиотек. [16]

Сохранение в памяти

С точки зрения ЦП машинный код хранится в оперативной памяти, но обычно также хранится в наборе кэшей по соображениям производительности. Для инструкций и данных могут быть разные кэши, в зависимости от архитектуры.

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

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

Процессору часто сообщают, с помощью разрешений на страницы в системе на основе страничного обмена, что текущая страница фактически содержит машинный код с помощью бита выполнения — страницы имеют несколько таких битов разрешения (чтение, запись и т. д.) для различных служебных функций. Например, в Unix-подобных системах страницы памяти можно переключить на исполнение с помощью mprotect()системного вызова, а в Windows VirtualProtect()можно использовать для достижения аналогичного результата. Если делается попытка выполнить машинный код на неисполняемой странице, обычно возникает ошибка, специфичная для архитектуры. Обработка данных как машинного кода или поиск новых способов использования существующего машинного кода с помощью различных методов является основой некоторых уязвимостей безопасности.

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

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

Удобочитаемость для людей

Существуют различные инструменты и методы для декодирования машинного кода обратно в соответствующий ему исходный код .

Машинный код можно легко декодировать обратно в соответствующий ему исходный код на языке ассемблера , поскольку язык ассемблера формирует однозначное соответствие с машинным кодом. [17] Метод декодирования языка ассемблера называется дизассемблированием .

Машинный код может быть декодирован обратно в соответствующий ему язык высокого уровня при выполнении двух условий:

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

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

Смотрите также

Примечания

  1. ^ ab В то время как перекрывающиеся инструкции на архитектурах процессоров с наборами инструкций переменной длины иногда можно организовать так, чтобы объединить различные пути кода в один посредством повторной синхронизации потока управления , перекрывающийся код для различных архитектур процессоров иногда можно также создать так, чтобы пути выполнения разветвлялись в разных направлениях в зависимости от базового процессора, как это иногда используется в толстых двоичных файлах .
  2. ^ Например, главные загрузочные записи (MBR) и загрузочные секторы DR-DOS (которые также содержат таблицу разделов и блок параметров BIOS , оставляя менее 446 и 423 байт для кода) традиционно могли самостоятельно находить загрузочный файл в файловой системе FAT12 или FAT16 и загружать его в память целиком, в отличие от их аналогов в DOS , MS-DOS и PC DOS , которые вместо этого полагаются на системные файлы , занимающие первые два места записи каталога в файловой системе, и первые три сектора IBMBIO.COM, которые хранятся в начале области данных в смежных секторах, содержащих вторичный загрузчик, для загрузки оставшейся части файла в память (требуя от SYS соблюдения всех этих условий). Когда была добавлена ​​поддержка FAT32 и логической адресации блоков (LBA), Microsoft даже перешла на требование инструкций i386 и разделила загрузочный код на два сектора из соображений размера кода, что было невозможным для DR-DOS, поскольку это нарушило бы обратную и кросс-совместимость с другими операционными системами в сценариях мультизагрузки и цепной загрузки , а также со старыми IBM PC–совместимыми ПК. Вместо этого загрузочные секторы DR-DOS 7.07 прибегли к самомодифицирующемуся коду , программированию на уровне опкода на машинном языке, контролируемому использованию (документированных) побочных эффектов , многоуровневому перекрытию данных/кода и алгоритмическим методам сворачивания , чтобы по-прежнему вмещать все в физический сектор размером всего 512 байт, не отказываясь ни от одной из своих расширенных функций.

Ссылки

  1. ^ Сталлингс, Уильям (2015). Организация и архитектура компьютеров 10-е издание . Pearson Prentice Hall. стр. 776. ISBN 9789332570405.
  2. ^ Челл, Брэдли. «Непосредственный операнд».
  3. ^ Дуриш, Пол (2004). Где действие: основы воплощенного взаимодействия. MIT Press . стр. 7. ISBN 0-262-54178-5. Получено 2023-03-05 .
  4. ^ Закс, Родней (1982). Программирование Z80 (Третье пересмотренное издание). Sybex . С. 67, 120, 609. ISBN 0-89588-094-6. Получено 2023-03-05 .
  5. ^ abcde Харрис, Дэвид; Харрис, Сара Л. (2007). Цифровое проектирование и архитектура компьютеров. Morgan Kaufmann Publishers . ISBN 978-0-12-370497-9. Получено 2023-03-05 .
  6. ^ abcde Jacob, Matthias; Jakubowski, Mariusz H.; Venkatesan, Ramarathnam [на Wikidata] (20–21 сентября 2007 г.). Towards Integral Binary Execution: Implementing Oblivious Hashing Using Overlapped Instruction Encodings (PDF) . Труды 9-го семинара по мультимедиа и безопасности (MM&Sec '07). Даллас, Техас, США: Association for Computing Machinery . стр. 129–140. CiteSeerX 10.1.1.69.5258 . doi :10.1145/1288869.1288887. ISBN  978-1-59593-857-2. S2CID  14174680. Архивировано (PDF) из оригинала 2018-09-04 . Получено 2021-12-25 .(12 страниц)
  7. ^ Lagarias, Jeffrey "Jeff" Clark ; Rains, Eric Michael ; Vanderbei, Robert J. (2009) [2001-10-13]. Brams, Stephen; Gehrlein, William V.; Roberts, Fred S. (ред.). "The Kruskal Count". Математика предпочтения, выбора и порядка. Эссе в честь Питера Дж. Фишберна . Берлин / Гейдельберг, Германия: Springer-Verlag : 371–391. arXiv : math/0110143 . ISBN 978-3-540-79127-0.(22 страницы)
  8. ^ Andriesse, Dennis; Bos, Herbert [в Wikidata] (2014-07-10). Написано в Vrije Universiteit Amsterdam, Амстердам, Нидерланды. Dietrich, Sven (ред.). Instruction-Level Steganography for Covert Trigger-Based Malware (PDF) . 11th International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment (DIMVA). Lecture Notes in Computer Science . Egham, UK; Switzerland: Springer International Publishing . pp. 41–50 [45]. doi :10.1007/978-3-319-08509-8_3. eISSN  1611-3349. ISBN 978-3-31908508-1. ISSN  0302-9743. S2CID  4634611. LNCS 8550. Архивировано (PDF) из оригинала 2023-08-26 . Получено 2023-08-26 .(10 страниц)
  9. ^ ab Jakubowski, Mariusz H. (февраль 2016 г.). "Graph Based Model for Software Tamper Protection". Microsoft . Архивировано из оригинала 2019-10-31 . Получено 2023-08-19 .
  10. ^ Jämthagen, Christopher (ноябрь 2016 г.). On Offensive and Defensive Methods in Software Security (PDF) (диссертация). Лунд, Швеция: Кафедра электротехники и информационных технологий, Лундский университет . стр. 96. ISBN 978-91-7623-942-1. ISSN  1654-790X. Архивировано (PDF) из оригинала 2023-08-26 . Получено 2023-08-26 .(1+xvii+1+152 страницы)
  11. ^ ab "Непреднамеренные инструкции на x86". Hacker News . 2021. Архивировано из оригинала 2021-12-25 . Получено 2021-12-24 .
  12. ^ Киндер, Йоханнес (24 сентября 2010 г.). Статический анализ исполняемых файлов x86 [ Statische Analysis von Programmen in x86 Maschinensprache ] (PDF) (Диссертация). Мюнхен, Германия: Технический университет Дармштадта . Д17. Архивировано из оригинала 12 ноября 2020 г. Проверено 25 декабря 2021 г.(199 страниц)
  13. ^ "Что такое обфускация "перекрывающихся инструкций"?". Обратный инжиниринг Stack Exchange . 2013-04-07. Архивировано из оригинала 2021-12-25 . Получено 2021-12-25 .
  14. Гейтс, Уильям «Билл» Генри , Личное общение.(Примечание. Согласно Якобу и др.)
  15. ^ Шахам, Ховав (2007). Геометрия невинной плоти на кости: возврат в libc без вызовов функций (на x86) (PDF) . Труды ACM, CCS 2007. ACM Press . Архивировано (PDF) из оригинала 2021-12-15 . Получено 2021-12-24 .
  16. ^ «Управляемый, неуправляемый, собственный: что это за код?». developer.com . 2003-04-28 . Получено 2008-09-02 .
  17. ^ Таненбаум, Эндрю С. (1990). Структурированная компьютерная организация, третье издание. Prentice Hall . стр. 398. ISBN 978-0-13-854662-5.
  18. ^ "Архитектура ассоциированных данных". Высокоуровневый ассемблер и функция набора инструментов .
  19. ^ "Содержимое файла COBOL SYSADATA". Enterprise COBOL для z/OS .
  20. ^ "Информация о сообщении SYSADATA". Информация Enterprise PL/I для z/OS 6.1 .
  21. ^ "Символы для отладки Windows". Microsoft Learn . 2022-12-20.
  22. ^ "Запрос файла .Pdb". Microsoft Learn . 2024-01-12.

Дальнейшее чтение