Машинный код
Выполнение программы |
---|
Общие понятия |
Типы кода |
Стратегии составления |
Известное время выполнения |
|
Известные компиляторы и наборы инструментов |
|
В программировании компьютерном машинный код — это компьютерный код , состоящий из машинного языка инструкций (ЦП) компьютера , которые используются для управления центральным процессором . Хотя десятичные компьютеры когда-то были обычным явлением, на современном рынке доминируют двоичные компьютеры ; для этих компьютеров машинный код — это «двоичное представление компьютерной программы, которая фактически читается и интерпретируется компьютером. Программа в машинном коде состоит из последовательности машинных инструкций (возможно, перемежающихся данными)». [1]
Каждая инструкция заставляет ЦП выполнять очень специфическую задачу, такую как загрузка, сохранение, переход или операция арифметико-логического устройства ЦП (АЛУ) с одной или несколькими единицами данных в регистрах или памяти .
Ранние процессоры имели специальный машинный код, который мог нарушить обратную совместимость с каждым новым выпущенным процессором. Понятие архитектуры набора команд (ISA) определяет и определяет поведение и кодирование в памяти набора команд системы без указания его точной реализации. Это действует как уровень абстракции, обеспечивая совместимость внутри одного семейства ЦП, так что машинный код, написанный или сгенерированный в соответствии с ISA для этого семейства, будет работать на всех ЦП в семействе, включая будущие ЦП.
В общем, каждое семейство архитектур (например, x86 , ARM ) имеет свой собственный ISA и, следовательно, свой собственный язык машинного кода. Есть исключения, такие как архитектура VAX , которая включала дополнительную поддержку набора инструкций PDP-11 и IA-64 , которая включала дополнительную поддержку набора инструкций IA-32 . Другим примером является PowerPC 615 , процессор, предназначенный для обработки инструкций PowerPC и x86.
Машинный код — это строго числовой язык и интерфейс самого низкого уровня к ЦП, предназначенный для программиста. Язык ассемблера обеспечивает прямое сопоставление числового машинного кода и удобочитаемой версии, в которой числовые коды операций и операнды заменяются читаемыми строками (например, 0x90 как инструкция NOP на x86 , где 0xB8 является инструкцией MOV, 0xE8 означает CALL или 0x0F05). для инструкции SYSCALL). Хотя можно писать программы непосредственно в машинном коде, управление отдельными битами и вычисление числовых адресов и констант вручную утомительно и подвержено ошибкам. По этой причине в современном контексте программы очень редко пишутся непосредственно в машинном коде, но могут выполняться для низкоуровневой отладки , исправления программ (особенно когда исходный код ассемблера недоступен) и дизассемблирования на языке ассемблера .
Большинство практических программ сегодня написаны на языках более высокого уровня . Эти программы либо переводятся в машинный код компилятором , либо интерпретируются интерпретатором , обычно после перевода в промежуточный код, например байт-код , который затем интерпретируется. [номер 1]
Машинный код по определению является самым низким уровнем детализации программирования, видимым программисту, но внутри многие процессоры используют микрокод или оптимизируют и преобразуют инструкции машинного кода в последовательности микроопераций . Микрокод и микрооперации обычно не считаются машинным кодом; за исключением некоторых машин, пользователь не может писать микрокод или микрооперации, а работа микрокода и преобразование инструкций машинного кода в микрооперации происходят прозрачно для программиста, за исключением побочных эффектов, связанных с производительностью.
Набор инструкций
[ редактировать ]Каждый процессор или семейство процессоров имеет свой собственный набор команд . Инструкции представляют собой наборы битов , цифр или символов, соответствующие машинным командам. Таким образом, набор команд специфичен для класса процессоров, использующих (в основном) одну и ту же архитектуру . Последующие или производные конструкции процессоров часто включают инструкции предшественника и могут добавлять новые дополнительные инструкции. Иногда в последующих разработках прекращается или изменяется значение некоторого кода инструкций (обычно потому, что он необходим для новых целей), что в некоторой степени влияет на совместимость кода; даже совместимые процессоры могут демонстрировать несколько иное поведение для некоторых инструкций, но это редко является проблемой. Системы также могут отличаться по другим деталям, таким как расположение памяти, операционные системы или периферийные устройства . Поскольку программа обычно зависит от таких факторов, разные системы обычно не будут запускать один и тот же машинный код, даже если используется процессор одного и того же типа.
Набор команд процессора может содержать инструкции фиксированной или переменной длины. Организация шаблонов зависит от конкретной архитектуры и типа инструкций. Большинство инструкций имеют одно или несколько полей кода операции , которые определяют основной тип инструкции (например, арифметическая, логическая, переход и т. д.), операцию (например, сложение или сравнение) и другие поля, которые могут указывать тип операнда ( например, сложение или сравнение). ), режим(ы) адресации , смещение(я) или индекс адресации или само значение операнда (такие постоянные операнды, содержащиеся в инструкции, называются непосредственными ). [2]
Не все машины или отдельные инструкции имеют явные операнды. На машине с одним аккумулятором аккумулятор неявно является одновременно левым операндом и результатом большинства арифметических инструкций. Некоторые другие архитектуры, такие как архитектура x86 , имеют аккумуляторные версии общих инструкций, при этом аккумулятор рассматривается как один из общих регистров для более длинных инструкций. Стековая машина имеет большинство или все свои операнды в неявном стеке. В инструкциях специального назначения также часто отсутствуют явные операнды; например, CPUID в архитектуре x86 записывает значения в четыре неявных регистра назначения. Это различие между явными и неявными операндами важно в генераторах кода, особенно в частях распределения регистров и отслеживания живого диапазона. Хороший оптимизатор кода может отслеживать как неявные, так и явные операнды, что может позволить более частое распространение констант , свертывание констант регистров (регистр, которому присвоен результат константного выражения, освобожденный путем замены его на эту константу) и другие улучшения кода.
Программы
[ редактировать ]Компьютерная программа — это список инструкций, которые может выполнять центральный процессор (ЦП). Выполнение программы осуществляется для того, чтобы процессор, который ее выполняет, решил проблему и, таким образом, достиг результата. В то время как простые процессоры способны выполнять инструкции одну за другой, суперскалярные процессоры способны при определенных обстоятельствах (когда конвейер заполнен) выполнять две или более инструкций одновременно. Например, оригинальный процессор Intel Pentium 1993 года может выполнять не более двух инструкций за такт, когда его конвейер заполнен.
На ход программы могут влиять специальные инструкции «перехода», которые передают выполнение по адресу (и, следовательно, инструкции), отличному от следующего по порядку числового адреса. Происходят ли эти условные переходы , зависит от такого условия, как значение, которое больше, меньше или равно другому значению.
Языки ассемблера
[ редактировать ]Гораздо более дружелюбная к человеку версия машинного языка, называемая языком ассемблера , использует мнемонические коды для ссылки на инструкции машинного кода, а не непосредственно на числовые значения инструкций, и использует символические имена для ссылки на места хранения, а иногда и на регистры . [3] Например, на процессоре Zilog Z80 машинный код 00000101
, что заставляет процессор уменьшать B
регистр общего назначения , будет представлен на языке ассемблера как DEC B
. [4]
Пример
[ редактировать ]Архитектура MIPS представляет собой конкретный пример машинного кода, длина инструкций которого всегда составляет 32 бита. [5] : 299 Общий тип инструкции определяется полем op (операция), старшими 6 битами. Инструкции J-типа (переход) и I-типа (немедленное выполнение) полностью определяются op . Инструкции R-типа (регистровые) включают дополнительную функцию поля для определения точной операции. В этих типах используются следующие поля:
6 5 5 5 5 6 bits [ op | rs | rt | rd |shamt| funct] R-type [ op | rs | rt | address/immediate] I-type [ op | target address ] J-type
rs , rt и rd обозначают операнды регистров; shamt дает сумму смены; а адрес или непосредственные поля непосредственно содержат операнд. [5] : 299–301
Например, сложение регистров 1 и 2 и помещение результата в регистр 6 кодируется: [5] : 554
[ op | rs | rt | rd |shamt| funct] 0 1 2 6 0 32 decimal 000000 00001 00010 00110 00000 100000 binary
Загрузите в регистр 8 значение, взятое из ячейки памяти через 68 ячеек после места, указанного в регистре 3: [5] : 552
[ op | rs | rt | address/immediate] 35 3 8 68 decimal 100011 00011 01000 00000 00001 000100 binary
Переходим по адресу 1024: [5] : 552
[ op | target address ] 2 1024 decimal 000010 00000 00000 00000 10000 000000 binary
Перекрывающиеся инструкции
[ редактировать ]На процессорных архитектурах с наборами команд переменной длины [6] (например, семейство процессоров Intel потока управления , x86 ), в пределах феномена ресинхронизации известного как счетчик Крускала , [7] [6] [8] [9] [10] иногда возможно посредством программирования на уровне кода операции намеренно организовать результирующий код так, чтобы два пути кода имели общий фрагмент последовательностей кодов операций. [номер 2] Они называются перекрывающимися инструкциями , перекрывающимися кодами операций , перекрывающимся кодом , перекрывающимся кодом , разделением инструкций или переходом в середину инструкции и представляют собой форму суперпозиции . [11] [12] [13]
В 1970-х и 1980-х годах для экономии места в памяти иногда использовались перекрывающиеся инструкции. Одним из примеров была реализация таблиц ошибок в Microsoft , Altair BASIC где чередующиеся инструкции совместно использовали свои байты инструкций. [14] [6] [11] Сегодня этот метод используется редко, но к нему все же может быть необходимо прибегнуть в тех областях, где необходима экстремальная оптимизация размера на уровне байтов, например, при реализации загрузчиков , которые должны вписываться в загрузочные сектора . [номер 3]
Его также иногда используют в качестве метода обфускации кода в качестве меры против дизассемблирования и взлома. [6] [9]
Этот принцип также используется в общих последовательностях кода «толстых» двоичных файлов , которые должны работать на нескольких процессорных платформах, несовместимых с набором команд. [номер 2]
Это свойство также используется для поиска непреднамеренных инструкций, называемых гаджетами, в существующих репозиториях кода и используется в возвратно-ориентированном программировании в качестве альтернативы внедрению кода для таких эксплойтов, как атаки с возвратом в libc . [15] [6]
Связь с микрокодом
[ редактировать ]В некоторых компьютерах машинный код архитектуры реализуется еще более фундаментальным базовым слоем, называемым микрокодом , обеспечивающим общий интерфейс машинного языка для линейки или семейства различных моделей компьютеров с совершенно разными базовыми потоками данных . Это сделано для облегчения портирования программ на машинном языке между разными моделями. Примером такого использования является IBM System/360 семейство компьютеров и их преемники. Имея ширину пути потока данных от 8 до 64 бит и выше, они, тем не менее, представляют собой общую архитектуру на уровне машинного языка по всей линии.
Использование микрокода для реализации эмулятора позволяет компьютеру представить архитектуру совершенно другого компьютера. Линия System/360 использовала это для возможности переноса программ с более ранних машин IBM на новое семейство компьютеров, например, эмулятор IBM 1401/1440/1460 на IBM S/360 модели 40.
Связь с байт-кодом
[ редактировать ]Машинный код обычно отличается от байт-кода (также известного как p-код), который либо выполняется интерпретатором, либо сам компилируется в машинный код для более быстрого (прямого) выполнения. Исключением являются случаи, когда процессор предназначен для использования определенного байт-кода непосредственно в качестве машинного кода, как в случае с процессорами Java .
Машинный код и ассемблерный код иногда называют собственным кодом , когда речь идет о зависящих от платформы частях языковых функций или библиотек. [16]
Хранение в памяти
[ редактировать ]С точки зрения ЦП машинный код хранится в оперативной памяти, но обычно он также хранится в наборе кэшей по соображениям производительности. В зависимости от архитектуры могут быть разные кэши для инструкций и данных.
ЦП знает, какой машинный код выполнить, на основе своего внутреннего счетчика программ. Программный счетчик указывает на адрес памяти и изменяется на основе специальных инструкций, которые могут вызывать программные ветвления. Счетчик программ обычно устанавливается на жестко запрограммированное значение при первом включении ЦП и, следовательно, будет выполнять любой машинный код, находящийся по этому адресу.
Аналогично, счетчик программы может быть настроен на выполнение любого машинного кода по произвольному адресу, даже если это недопустимый машинный код. Обычно это вызывает сбой защиты, зависящий от архитектуры.
ЦП часто сообщается с помощью разрешений страницы в системе, основанной на подкачке, если текущая страница действительно содержит машинный код с помощью бита выполнения — страницы имеют несколько таких битов разрешений (доступных для чтения, записи и т. д.) для различных служебных функций. Например, в Unix-подобных системах страницы памяти можно сделать исполняемыми с помощью команды mprotect()
системный вызов, а в Windows VirtualProtect()
можно использовать для достижения аналогичного результата. Если предпринимается попытка выполнить машинный код на неисполняемой странице, обычно возникает ошибка, специфичная для архитектуры. Обработка данных как машинного кода или поиск новых способов использования существующего машинного кода с помощью различных методов является основой некоторых уязвимостей безопасности.
Аналогично, в системе на основе сегментов дескрипторы сегментов могут указывать, может ли сегмент содержать исполняемый код и в каких кольцах этот код может выполняться.
С точки зрения процесса , пространство кода — это часть его адресного пространства , где хранится исполняемый код. В многозадачных программы системах это включает в себя сегмент кода и обычно разделяемые библиотеки . В многопоточной среде разные потоки одного процесса совместно используют пространство кода и пространство данных, что значительно снижает затраты на переключение контекста по сравнению с переключением процессов.
Читабельность для людей
[ редактировать ]Существуют различные инструменты и методы для декодирования машинного кода обратно в соответствующий исходный код .
Машинный код можно легко декодировать обратно в соответствующий исходный код на языке ассемблера , поскольку язык ассемблера обеспечивает однозначное сопоставление с машинным кодом. [17] Метод декодирования языка ассемблера называется дизассемблированием .
Машинный код может быть декодирован обратно на соответствующий язык высокого уровня при двух условиях:
Первое условие — принять запутанное чтение исходного кода. Запутанная версия исходного кода отображается, если машинный код отправляется декомпилятору исходного языка.
Второе условие требует, чтобы машинный код содержал закодированную информацию об исходном коде. Информация включает таблицу символов , содержащую символы отладки . Таблица символов может храниться в исполняемом файле или существовать в отдельных файлах. Затем отладчик может прочитать таблицу символов , чтобы помочь программисту в интерактивном режиме отладить машинный код во время выполнения .
- Операционная система SHARE (1959 г.) для компьютеров IBM 709 , IBM 7090 и IBM 7094 позволяла использовать формат загружаемого кода, называемый SQUOZE . SQUOZE представлял собой сжатую двоичную форму кода языка ассемблера и включал таблицу символов.
- для мэйнфреймов IBM Современные операционные системы , такие как z/OS , имеют таблицу символов, называемую «Связанные данные» (ADATA). Таблица хранится в файле, который может быть создан IBM High-Level Assembler (HLASM). [18] от IBM , COBOL компилятор [19] и компилятор PL/I от IBM . [20]
- Microsoft Windows имеет таблицу символов [21] который хранится в базе данных программы (
.pdb
) файл. [22] - Большинство Unix-подобных операционных систем имеют доступные форматы таблиц символов, называемые STABS и DWARF . В macOS и других операционных системах на базе Darwin символы отладки хранятся в формате DWARF в отдельном
.dSYM
файл.
См. также
[ редактировать ]- Язык ассемблера
- Порядок байтов
- Список машинных языков
- Монитор машинного кода
- Накладной код
- машина P-кода
- Вычисления с сокращенным набором команд (RISC)
- Очень длинное командное слово
- Код обучающей машины: Микропрофессор MPF-I
Примечания
[ редактировать ]- ^ Например, многие версии BASIC , особенно ранние, а также Smalltalk , MATLAB , Perl , Python , Ruby и другие языки специального назначения или скриптовые языки .
- ^ Jump up to: а б Хотя перекрывающиеся инструкции на процессорных архитектурах с наборами команд переменной длины потока управления иногда могут быть организованы так, чтобы объединить разные пути выполнения кода обратно в один посредством ресинхронизации , иногда также может быть создан перекрывающийся код для разных процессорных архитектур, чтобы заставить пути выполнения разветвляться в разных направлениях. в зависимости от базового процессора, что иногда используется в «толстых» двоичных файлах .
- ^ Например, DR-DOS MBR и загрузочные секторы (которые также содержат таблицу разделов и блок параметров BIOS , оставляя для кода менее 446 или 423 байта) традиционно могли найти загрузочный файл в FAT12 или FAT16 файле . системы сами по себе и загружают ее в память целиком, в отличие от своих аналогов в MS-DOS / PC DOS , которые вместо этого полагались на то, что системные файлы занимали первые две записи каталога файловой системы и первые три сектора IBMBIO. .COM будет храниться в начале области данных в смежных секторах, содержащих вторичный загрузчик для загрузки оставшейся части файла в память (требуется, чтобы SYS позаботился обо всех этих условиях). Когда была добавлена поддержка FAT32 и LBA , Microsoft даже перешла на требование 386 инструкций и разделила загрузочный код на два сектора из соображений размера кода, чего нельзя было следовать для DR-DOS, поскольку это привело бы к нарушению обратной совместимости и кросс-совместимости с другие операционные системы в мультизагрузке и цепной загрузке сценариях, а также на старых ПК . Вместо этого загрузочные сектора DR-DOS 7.07 прибегали к самомодифицирующемуся коду , программированию на уровне опкода на машинном языке, контролируемому использованию (документированных) побочных эффектов , многоуровневому перекрытию данных/кода и методам алгоритмического свертывания , чтобы по-прежнему уместить все в единое целое. физический сектор размером всего 512 байт, не отказываясь при этом от своей расширенной функциональности.
Ссылки
[ редактировать ]- ^ Столлингс, Уильям. Компьютерная организация и архитектура, 10-е издание . п. 776. ИСБН 9789332570405 .
- ^ Кьелл, Брэдли. «Непосредственный операнд» .
- ^ Дуриш, Пол (2004). Где действие: основы воплощенного взаимодействия . МТИ Пресс . п. 7. ISBN 0-262-54178-5 . Проверено 05 марта 2023 г.
- ^ Закс, Родней (1982). Программирование Z80 (Третье исправленное издание). Сайбекс . стр. 67, 120, 609. ISBN. 0-89588-094-6 . Проверено 05 марта 2023 г.
- ^ Jump up to: а б с д и Харрис, Дэвид; Харрис, Сара Л. (2007). Цифровой дизайн и компьютерная архитектура . Издательство Морган Кауфманн . ISBN 978-0-12-370497-9 . Проверено 05 марта 2023 г.
- ^ Jump up to: а б с д и Джейкоб, Матиас; Якубовский, Мариуш Х.; Венкатесан, Рамаратнам [в Викиданных] (20–21 сентября 2007 г.). На пути к интегральному двоичному исполнению: реализация забывчивого хеширования с использованием перекрывающихся кодировок инструкций (PDF) . Материалы 9-го семинара по мультимедиа и безопасности (MM&Sec '07). Даллас, Техас, США: Ассоциация вычислительной техники . стр. 129–140. CiteSeerX 10.1.1.69.5258 . дои : 10.1145/1288869.1288887 . ISBN 978-1-59593-857-2 . S2CID 14174680 . Архивировано (PDF) из оригинала 4 сентября 2018 г. Проверено 25 декабря 2021 г. (12 страниц)
- ^ Лагариас, Джеффри «Джефф» Кларк ; Рейнс, Эрик Майкл ; Вандербей, Роберт Дж. (2009) [13 октября 2001 г.]. Брамс, Стивен; Герляйн, Уильям В.; Робертс, Фред С. (ред.). «Граф Крускал». Математика предпочтения, выбора и порядка. Очерки в честь Питера Дж. Фишберна . Берлин / Гейдельберг, Германия: Springer-Verlag : 371–391. arXiv : math/0110143 . ISBN 978-3-540-79127-0 . (22 страницы)
- ^ Андрисс, Деннис; Бос, Герберт [в Викиданных] (10 июля 2014 г.). Написано в Vrije Universiteit Amsterdam, Амстердам, Нидерланды. Дитрих, Свен (ред.). Стеганография на уровне инструкций для скрытых вредоносных программ, основанных на триггерах (PDF) . 11-я Международная конференция по обнаружению вторжений и вредоносного ПО и оценке уязвимостей (DIMVA). Конспекты лекций по информатике . Эгам, Великобритания; Швейцария: Международное издательство Springer . С. 41–50 [45]. дои : 10.1007/978-3-319-08509-8_3 . eISSN 1611-3349 . ISBN 978-3-31908508-1 . ISSN 0302-9743 . S2CID 4634611 . LNCS 8550. Архивировано (PDF) из оригинала 26 августа 2023 г. Проверено 26 августа 2023 г. (10 страниц)
- ^ Jump up to: а б Якубовский, Мариуш Х. (февраль 2016 г.). «Графовая модель защиты программного обеспечения от несанкционированного доступа» . Майкрософт . Архивировано из оригинала 31 октября 2019 г. Проверено 19 августа 2023 г.
- ^ Ямтаген, Кристофер (ноябрь 2016 г.). «О наступательных и защитных методах безопасности программного обеспечения» (PDF) (Диссертация). Лунд, Швеция: Факультет электротехники и информационных технологий Лундского университета . п. 96. ИСБН 978-91-7623-942-1 . ISSN 1654-790X . Архивировано (PDF) из оригинала 26 августа 2023 г. Проверено 26 августа 2023 г. (1+xvii+1+152 страницы)
- ^ Jump up to: а б «Непредусмотренные инструкции на x86» . Хакерские новости . 2021. Архивировано из оригинала 25 декабря 2021 г. Проверено 24 декабря 2021 г.
- ^ Дети, Джон (24 сентября 2010 г.). Статический анализ исполняемых файлов x86 [ Статический анализ программ на машинном языке x86 ] (PDF) (кандидатская диссертация). Мюнхен, Германия: Технический университет Дармштадта . Д17. Архивировано из оригинала 12 ноября 2020 г. Проверено 25 декабря 2021 г. (199 страниц)
- ^ «Что такое запутывание «перекрывающихся инструкций»?» . Обмен стеками реверс-инжиниринга . 07.04.2013. Архивировано из оригинала 25 декабря 2021 г. Проверено 25 декабря 2021 г.
- ^ Гейтс, Уильям «Билл» Генри , личное сообщение (примечание: согласно Джейкобу и др .)
- ^ Шахам, Ховав (2007). Геометрия невинной плоти на костях: возврат в libc без вызовов функций (на x86) (PDF) . Труды ACM, CCS 2007. ACM Press . Архивировано (PDF) из оригинала 15 декабря 2021 г. Проверено 24 декабря 2021 г.
- ^ «Управляемый, неуправляемый, собственный: что это за код?» . разработчик.com . 28 апреля 2003 г. Проверено 2 сентября 2008 г.
- ^ Таненбаум, Эндрю С. (1990). Структурированная компьютерная организация, третье издание . Прентис Холл. п. 398 . ISBN 978-0-13-854662-5 .
- ^ «Связанная архитектура данных» . Функция ассемблера высокого уровня и инструментария .
- ^ «Содержимое файла COBOL SYSADATA» . Корпоративный COBOL для z/OS .
- ^ «Информация о сообщении SYSADATA» . Информация о Enterprise PL/I для z/OS 6.1 .
- ^ «Символы для отладки Windows» . Microsoft Learn .
- ^ «Запрос файла .Pdb» . Microsoft Learn .
Дальнейшее чтение
[ редактировать ]- Хеннесси, Джон Л .; Паттерсон, Дэвид А. (1994). Компьютерная организация и дизайн. Аппаратно-программный интерфейс . Издательство Морган Кауфманн . ISBN 1-55860-281-Х .
- Таненбаум, Эндрю С. (1999). Структурированная компьютерная организация . Прентис Холл . ISBN 0-13-020435-8 .
- Брукшир, Дж. Гленн (2007). Информатика: обзор . Эддисон Уэсли . ISBN 978-0-321-38701-1 .