Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

Язык ассемблера x86 - это семейство обратно совместимых языков ассемблера , которые обеспечивают определенный уровень совместимости вплоть до Intel 8008, представленного в апреле 1972 года. [1] [2] Языки ассемблера x86 используются для создания объектного кода для x86. класс процессоров. Как и все языки ассемблера, он использует краткую мнемонику для представления основных инструкций, которые ЦП компьютера может понять и выполнить. [3] Компиляторы иногда создают ассемблерный код в качестве промежуточного шага при переводе высокоуровневой программы в машинный код.. Кодирование на ассемблере, рассматриваемое как язык программирования , является машинно-зависимым и низкоуровневым . Языки ассемблера чаще используются для подробных и критичных по времени приложений, таких как небольшие встроенные системы реального времени или ядра операционных систем и драйверы устройств .

Мнемоника и коды операций [ править ]

Каждая инструкция сборки x86 представлена мнемоникой, которая, часто в сочетании с одним или несколькими операндами, преобразуется в один или несколько байтов, называемых кодом операции ; NOP инструкция переводится 0x90, например , и HLT инструкция переводится 0xF4. [3] Существуют потенциальные коды операций без задокументированной мнемоники, которые разные процессоры могут интерпретировать по-разному, заставляя программу, использующую их, вести себя непоследовательно или даже генерировать исключение на некоторых процессорах. Эти коды операций часто используются в соревнованиях по написанию кода как способ сделать код меньше, быстрее, элегантнее или просто продемонстрировать мастерство автора.

Синтаксис [ править ]

x86 ассемблер имеет два основных синтаксис ветви: Intel синтаксис и AT & T синтаксис . [4] Синтаксис Intel доминирует в мире DOS и Windows , а синтаксис AT&T доминирует в мире Unix , поскольку Unix была создана в AT&T Bell Labs . [5] Ниже приводится краткое описание основных различий между синтаксисом Intel и AT & T синтаксис :

Многие ассемблеры x86 используют синтаксис Intel , включая NASM , FASM , MASM , TASM и YASM . GAS , который изначально использовал синтаксис AT&T , поддерживает оба синтаксиса начиная с версии 2.10 через директиву .intel_syntax . [4] [6] [7] Особенность синтаксиса AT&T для x86 состоит в том, что операнды x87 перевернуты, что является унаследованной ошибкой от исходного ассемблера AT&T. [8]

Синтаксис AT&T почти универсален для всех других архитектур с таким же movпорядком; Изначально это был синтаксис сборки PDP-11. Синтаксис Intel специфичен для архитектуры x86 и используется в документации платформы x86.

Регистры [ править ]

Процессоры x86 имеют набор регистров, доступных для использования в качестве хранилищ двоичных данных. В совокупности регистры данных и адреса называются общими регистрами. Каждый регистр имеет особую цель в дополнение к тому, что они все могут делать: [3]

  • AX умножение / деление, загрузка и сохранение строки
  • Регистр индекса BX для MOVE
  • Счетчик CX для строковых операций и сдвигов
  • Адрес порта DX для IN и OUT
  • SP указывает на вершину стека
  • BP указывает на основание кадра стека
  • SI указывает на источник в потоковых операциях
  • DI указывает на пункт назначения в потоковых операциях

Наряду с общими регистрами дополнительно:

  • Указатель инструкции IP
  • ФЛАГИ
  • регистры сегментов (CS, DS, ES, FS, GS, SS), которые определяют, где начинается сегмент 64k ​​(нет FS и GS в 80286 и ранее)
  • Регистры дополнительных расширений ( MMX , 3DNow! , SSE , и т.д.) (Pentium и более поздние версии).

Регистр IP указывает на смещение памяти следующей инструкции в сегменте кода (он указывает на первый байт инструкции). Программист не может получить доступ к регистру IP напрямую.

Регистры x86 можно использовать с помощью инструкций MOV . Например, в синтаксисе Intel:

mov  ax ,  1234h  ; копирует значение 1234hex (4660d) в регистр AX
mov  bx ,  ax  ; копирует значение регистра AX в регистр BX

Сегментированная адресация [ править ]

Архитектура x86 в реальном и виртуальном режиме 8086 использует процесс, известный как сегментация для адресации памяти, а не модель плоской памяти, используемую во многих других средах. Сегментация включает составление адреса памяти из двух частей, сегмента и смещения ; сегмент указывает на начало 64 КБ (64 × 2 10) группа адресов и смещение определяет, насколько далеко от этого начального адреса находится желаемый адрес. При сегментированной адресации для полного адреса памяти требуются два регистра. Один для удержания сегмента, другой для удержания смещения. Чтобы преобразовать обратно в плоский адрес, значение сегмента сдвигается на четыре бита влево (эквивалент умножения на 2, 4 или 16), затем добавляется к смещению, чтобы сформировать полный адрес, что позволяет преодолеть барьер в 64 КБ за счет умного выбора адресов. , хотя это значительно усложняет программирование.

В режиме реального времени / защищен только, например, если DS содержит шестнадцатеричное число 0xDEAD и DX содержит номер 0xCAFE они вместе указывать на адрес памяти 0xDEAD * 0x10 + 0xCAFE = 0xEB5CE. Таким образом, ЦП может адресовать до 1 048 576 байт (1 МБ) в реальном режиме. Комбинируя значения сегмента и смещения, мы находим 20-битный адрес.

Исходный IBM PC ограничивал программы до 640 КБ, но для реализации схемы переключения банков использовалась расширенная спецификация памяти, которая вышла из употребления, когда более поздние операционные системы, такие как Windows, использовали более широкие диапазоны адресов новых процессоров и реализовали свою собственную виртуальную память. схемы.

Защищенный режим, начиная с Intel 80286, использовался OS / 2 . Несколько недостатков, таких как невозможность доступа к BIOS и невозможность вернуться в реальный режим без перезагрузки процессора, препятствовали широкому использованию. [9] 80286 также по-прежнему ограничивался адресацией памяти в 16-битных сегментах, что означало, что одновременно можно было получить доступ только к 2 16 байтам (64 килобайта ). Чтобы получить доступ к расширенным функциям 80286, операционная система установила бы процессор в защищенный режим, разрешив 24-битную адресацию и, таким образом, 2 24 байта памяти (16 мегабайт ).

В защищенном режиме селектор сегмента может быть разбит на три части: 13-битный индекс, бит индикатора таблицы, который определяет, находится ли запись в GDT или LDT, и 2-битный запрашиваемый уровень привилегий ; см. сегментацию памяти x86 .

При ссылке на адрес с сегментом и смещением используется обозначение сегмента : смещение , поэтому в приведенном выше примере плоский адрес 0xEB5CE может быть записан как 0xDEAD: 0xCAFE или как пара регистров сегмента и смещения; ДС: DX.

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

  • CS: IP (CS - сегмент кода , IP - указатель инструкции ) указывает на адрес, по которому процессор будет извлекать следующий байт кода.
  • SS: SP (SS - сегмент стека , SP - указатель стека ) указывает на адрес вершины стека, то есть на самый последний переданный байт.
  • DS: SI (DS - сегмент данных , SI - индекс источника ) часто используется для указания на строковые данные, которые собираются скопировать в ES: DI.
  • ES: DI (ES - дополнительный сегмент , DI - индекс назначения ) обычно используется для указания на место назначения для копии строки, как упоминалось выше.

Intel 80386 имеет три рабочих режима: реальный режим, защищенный режим и виртуальный режим. Защищенный режим , который дебютировал в 80286 был расширен , чтобы позволить 80386 адресовать до 4 Гб оперативной памяти, весь новый виртуальный режим 8086 ( VM86 ) позволили запустить один или несколько реальных программы режима в защищенной среде , которая в значительной степени эмулировать реальный режим, хотя некоторые программы были несовместимы (обычно в результате уловок с адресацией памяти или использования неопределенных кодов операций).

32-битная модель плоской памяти о 80386 «ы расширенного защищенный режим может быть наиболее важным изменением функции для семейства процессоров x86 , пока AMD не выпустил x86-64 в 2003 году, как это помогло управлять широкомасштабным принятием Windows 3.1 (которая опиралась на защищенный режим), поскольку теперь Windows может запускать сразу несколько приложений, включая приложения DOS, с помощью виртуальной памяти и простой многозадачности.

Режимы выполнения [ править ]

Процессоры x86 поддерживают пять режимов работы для кода x86: Real Mode , Protected Mode , Long Mode , Virtual 86 Mode и System Management Mode , в которых одни инструкции доступны, а другие нет. 16-разрядное подмножество инструкций доступно на 16-разрядных процессорах x86, а именно 8086, 8088, 80186, 80188 и 80286. Эти инструкции доступны в реальном режиме на всех процессорах x86 и в 16-разрядном защищенном режиме. ( Начиная с 80286 ) доступны дополнительные инструкции, относящиеся к защищенному режиму. На 80386и позже, 32-битные инструкции (включая более поздние расширения) также доступны во всех режимах, включая реальный режим; на этих процессорах добавлены режим V86 и 32-битный защищенный режим с дополнительными инструкциями, предоставленными в этих режимах для управления их функциями. SMM с некоторыми собственными специальными инструкциями доступен на некоторых процессорах Intel i386SL, i486 и более поздних версиях. Наконец, в длинном режиме ( начиная с AMD Opteron ) также доступны 64-битные инструкции и другие регистры. Набор команд одинаков в каждом режиме, но адресация памяти и размер слова различаются, что требует разных стратегий программирования.

Режимы, в которых может выполняться код x86:

  • Реальный режим (16 бит)
    • 20-битное сегментированное адресное пространство памяти (это означает, что можно адресовать только 1 МБ памяти - на самом деле, немного больше), прямой программный доступ к периферийному оборудованию и отсутствие концепции защиты памяти или многозадачности на аппаратном уровне. В этом режиме запускаются компьютеры, использующие BIOS .
  • Защищенный режим (16 бит и 32 бит)
    • Расширяет адресуемую физическую память до 16 МБ и адресуемую виртуальную память до 1 ГБ . Предоставляет уровни привилегий и защищенную память , что предотвращает разрушение программ друг друга. В 16-битном защищенном режиме (использовавшемся в конце эры DOS ) использовалась сложная, многосегментная модель памяти. В 32-битном защищенном режиме используется простая плоская модель памяти.
  • Длинный режим (64-бит)
    • В основном это расширение набора 32-битных (защищенный режим) инструкций, но в отличие от перехода с 16 на 32 бит, многие инструкции были отброшены в 64-битном режиме. Первопроходец AMD .
  • Виртуальный режим 8086 (16 бит)
    • Специальный гибридный режим работы, который позволяет программам и операционным системам реального режима работать под управлением операционной системы супервизора защищенного режима.
  • Режим управления системой (16 бит)
    • Обрабатывает общесистемные функции, такие как управление питанием, управление системным оборудованием и собственный код, разработанный OEM. Он предназначен для использования только системной прошивкой. Все нормальное выполнение, включая операционную систему , приостанавливается. Затем выполняется альтернативная программная система (которая обычно находится во встроенном ПО компьютера или в аппаратном отладчике ) с высокими привилегиями.

Переключение режимов [ править ]

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

Примеры [ править ]

На компьютере с устаревшей версией BIOS BIOS и загрузчик работают в реальном режиме , затем ядро ​​64-разрядной операционной системы проверяет и переключает ЦП в длительный режим, а затем запускает новые потоки режима ядра, выполняющие 64-разрядный код.

С компьютера под управлением UEFI , с прошивкой UEFI ( за исключением КСМ и унаследованной Option ROM ), в UEFI загрузчика и ядра операционной системы UEFI все работает в режиме Long.

Типы инструкций [ править ]

В общем, особенности современного набора инструкций x86 :

  • Компактная кодировка
    • Переменная длина и независимость от выравнивания (закодированы с прямым порядком байтов , как и все данные в архитектуре x86)
    • В основном одноадресные и двухадресные инструкции, то есть первый операнд также является адресатом.
    • Поддерживаются операнды памяти в качестве источника и назначения (часто используются для чтения / записи элементов стека, адресованных с использованием небольших немедленных смещений).
    • Как общее, так и неявное использование регистров ; хотя все семь (подсчет ebp) общих регистров в 32-битном режиме и все пятнадцать (подсчет rbp) общих регистров в 64-битном режиме могут свободно использоваться в качестве аккумуляторов или для адресации, большинство из них также неявно используются некоторыми (более или меньше) специальные инструкции; поэтому затронутые регистры должны быть временно сохранены (обычно в стеке), если они активны во время таких последовательностей команд.
  • Создает условные флаги неявно через большинство целочисленных инструкций ALU .
  • Поддерживает различные режимы адресации, включая немедленный, смещение и масштабированный индекс, но не относительно ПК, за исключением переходов (введенных как улучшение в архитектуре x86-64 ).
  • Включает плавающую точку в стек регистров.
  • Содержит специальную поддержку для атомных чтения-модификация-запись инструкций ( xchg, cmpxchg/ cmpxchg8b, xaddи целого числа команд , которые комбинируют с lockпрефиксом)
  • SIMD- инструкции (инструкции, которые выполняют параллельные одновременные одиночные инструкции для многих операндов, закодированных в соседних ячейках более широких регистров).

Инструкции по стеку [ править ]

Архитектура x86 имеет аппаратную поддержку механизма стека выполнения. Инструкции , такие как push, pop, callи retиспользуются с правильно настроить стек для передачи параметров, чтобы выделить место для локальных данных, а также для сохранения и восстановления вызова возврата очков. ret Размер инструкция очень полезна для реализации космических эффективные (и быстро) соглашений о вызовах , где вызываемое несет ответственность за освоение пространства стека заняты параметрами.

При настройке кадра стека для хранения локальных данных рекурсивной процедуры есть несколько вариантов; высокий уровень enterинструкции (введен с 80186) занимает процедура гнездящихся-глубины аргумента, а также локальный размер аргумент, и может быть быстрее , чем более явной манипуляции регистров (таких как push bp ; mov bp, sp ; ). Будет ли он быстрее или медленнее, зависит от конкретной реализации процессора x86, а также от соглашения о вызовах, используемого компилятором, программистом или конкретным программным кодом; большая часть кода x86 предназначена для работы на процессорах x86 от нескольких производителей и на разных технологических поколениях процессоров, что подразумевает сильно различающиесяsub sp, sizeмикроархитектуры и решения микрокода, а также различные варианты дизайна на уровне затвора и транзистора .

Полный диапазон режимов адресации (включая немедленную адресацию и базовое + смещение ) даже для таких инструкций, как pushи pop, упрощает прямое использование стека для целочисленных данных , данных с плавающей запятой и адресных данных, а также сохраняет спецификации и механизмы ABI относительно простыми по сравнению с некоторые архитектуры RISC (требуют более подробных сведений о стеке вызовов).

Целочисленные инструкции ALU [ править ]

x86 сборка имеет стандартные математические операции, add, sub, mul, с idiv; эти логические операторы and , or, xor, neg; битовая арифметика и логика , sal/ sar, shl/ shr; Поворот с и без переноса, rcl/ rcr, rol/ ror, дополнение BCD арифметических команд, aaa, aad, daaи др.

Инструкции с плавающей точкой [ править ]

Язык ассемблера x86 включает инструкции для стекового модуля с плавающей запятой (FPU). FPU был дополнительным отдельным сопроцессором для 8086–80386, он был встроен в чип для серии 80486 и является стандартной функцией в каждом процессоре Intel x86, начиная с 80486, начиная с Pentium. Инструкции FPU включают в себя сложение, вычитание, отрицание, умножение, деление, остаток, квадратные корни, целочисленное усечение, дробное усечение и масштабирование по степени двойки. Операции также включают инструкции преобразования, которые могут загружать или сохранять значение из памяти в любом из следующих форматов: десятичное с двоичным кодом, 32-разрядное целое число, 64-разрядное целое число, 32-разрядное число с плавающей запятой, 64-разрядное число с плавающей запятой. запятой или 80-битной с плавающей запятой (при загрузке значение преобразуется в текущий используемый режим с плавающей запятой).x86 также включает ряд трансцендентных функций, включая синус, косинус, тангенс, арктангенс, возведение в степень с основанием 2 и логарифмы с основанием 2, 10 илие .

Регистр стека в формат стек регистра инструкций, как правило , или , где эквивалентно , и является одним из регистров стека 8 ( , ..., ). Как и целые числа, первый операнд является одновременно первым операндом источника и операндом назначения. и должны быть выделены как первая замена исходных операндов перед выполнением вычитания или деления. Инструкции сложения, вычитания, умножения, деления, сохранения и сравнения включают в себя режимы команд, которые выталкивают верхнюю часть стека после завершения их операции. Так, например, выполняет вычисление , затем удаляет его из вершины стека, таким образом делая результат вfop st, st(n)fop st(n), ststst(0)st(n)st(0)st(1)st(7)fsubrfdivrfaddp st(1), stst(1) = st(1) + st(0)st(0)st(1)верх стопки в st(0).

Инструкции SIMD [ править ]

Современные процессоры x86 содержат инструкции SIMD , которые в основном выполняют одну и ту же операцию параллельно со многими значениями, закодированными в широком регистре SIMD. Различные технологии команд поддерживают разные операции с разными наборами регистров, но, взятые как единое целое (от MMX до SSE4.2 ), они включают общие вычисления по целочисленной арифметике или арифметике с плавающей запятой (сложение, вычитание, умножение, сдвиг, минимизация, максимизация, сравнение, деление или квадратный корень). Так, например, paddw mm0, mm1выполняет 4 параллельных 16-битных (обозначенных w) целых чисел, складывает (обозначено padd) mm0значений mm1и сохраняет результат в mm0. Потоковые расширения SIMDили SSE также включает режим с плавающей запятой, в котором фактически изменяется только самое первое значение регистров (раскрывается в SSE2 ). Были добавлены некоторые другие необычные инструкции, включая сумму абсолютных разностей (используется для оценки движения при сжатии видео , например, в MPEG ) и 16-битную инструкцию умножения (полезно для программного альфа-смешивания и цифровой фильтрации ) . SSE (начиная с SSE3 ) и 3DNow! расширения включают инструкции сложения и вычитания для обработки парных значений с плавающей запятой как комплексных чисел.

Эти наборы команд также включают в себя многочисленные фиксированные инструкции подслова для перетасовки, вставки и извлечения значений внутри регистров. Кроме того, есть инструкции для перемещения данных между целочисленными регистрами и регистрами XMM (используется в SSE) / FPU (используется в MMX).

Инструкции по работе с данными [ править ]

Процессор x86 также включает в себя сложные режимы адресации для адресации памяти с немедленным смещением, регистр, регистр со смещением, масштабированный регистр со смещением или без него, а также регистр с дополнительным смещением и другой масштабированный регистр. Так, например, можно закодировать mov eax, [Table + ebx + esi*4]как одну команду, которая загружает 32 бита данных из адреса, вычисленного как (Table + ebx + esi * 4)смещение от dsселектора, и сохраняет их в eaxрегистре. Как правило, процессоры x86 могут загружать и использовать память, соответствующую размеру любого регистра, с которым он работает. (Инструкции SIMD также включают инструкции половинной загрузки.)

Набор команд x86 , включает в себя строковую нагрузку, хранить, перемещать, сканированию и сравнить инструкции ( lods, stos, movs, scasи cmps) , которые выполняют каждую операцию до заданного размера ( bдля 8-битового байта, wдля 16-битного слова, dдля 32-битных двойного слова) затем увеличивает / уменьшает (в зависимости от DF, флага направления) неявный адресный регистр ( siдля lods, diдля stosи scas, а также для movsи cmps). Для операций загрузки, сохранения и сканирования неявный регистр цели / источника / сравнения находится в регистре al, axили eax(в зависимости от размера). Используются неявные сегментные регистры dsдля siи esдля di. cxилиecxРегистр используется как уменьшающий счетчик, и операция останавливается, когда счетчик достигает нуля или (для сканирований и сравнений), когда обнаруживается неравенство.

Стек реализуется с помощью неявно уменьшающегося (push) и увеличивающегося (pop) указателя стека. В 16-битном режиме этот неявный указатель стека адресуется как SS: [SP], в 32-битном режиме это SS: [ESP], а в 64-битном режиме это [RSP]. Указатель стека фактически указывает на последнее сохраненное значение в предположении, что его размер будет соответствовать рабочему режиму процессора (т.е. 16, 32 или 64 бита), чтобы соответствовать ширине инструкций push/ pop/ call/ по умолчанию ret. Также включены инструкции enterи leaveкакие данные запаса и удалить из верхней части стеки, настраивая указатель кадра стека в bp/ ebp/ rbp. Однако прямая установка или сложение и вычитание sp/ esp/rspregister также поддерживается, поэтому инструкции enter/ leaveобычно не нужны.

Этот код в начале функции:

 нажать  ebp  ; сохранить кадр стека вызывающей функции (ebp)  mov  ebp ,  esp  ; создать новый кадр стека поверх  sub  esp ,  4  стека вызывающего объекта ; выделить 4 байта пространства стека для локальных переменных этой функции

... функционально эквивалентен просто:

 введите  4 ,  0

Другие инструкции для управления стеком включают pushf/ popfдля сохранения и извлечения регистра (E) FLAGS. В pusha/ popaинструкциях будут хранить и извлекать все состояние целочисленного регистра и из стека.

Предполагается, что значения для загрузки или сохранения SIMD упакованы в соседние позиции для регистра SIMD и будут выравнивать их в последовательном порядке с прямым порядком байтов. Некоторые инструкции загрузки и сохранения SSE требуют 16-байтового выравнивания для правильной работы. Наборы команд SIMD также включают в себя инструкции «предварительной выборки», которые выполняют загрузку, но не нацелены на какой-либо регистр, используемый для загрузки кеша. Наборы инструкций SSE также включают в себя инструкции невременного хранения, которые будут выполнять операции сохранения прямо в память без выполнения выделения кэша, если место назначения еще не кэшировано (в противном случае оно будет вести себя как обычное хранилище).

Большинство общих инструкций с целыми числами и с плавающей запятой (но без SIMD) могут использовать один параметр в качестве сложного адреса в качестве второго параметра источника. Целочисленные инструкции также могут принимать один параметр памяти в качестве операнда назначения.

Ход программы [ править ]

Сборка x86 имеет операцию безусловного перехода jmp, которая может принимать в качестве параметра немедленный адрес, регистр или косвенный адрес (обратите внимание, что большинство процессоров RISC поддерживают только регистр связи или короткое немедленное смещение для перехода).

Также поддерживаются несколько условных переходов, в том числе jz(переход на ноль), jnz(переход на ненулевое значение), jg(переход на большее, чем, со знаком), jl(переход на меньшее, чем, со знаком), ja(переход на выше / большее, чем, без знака) , jb(перейти ниже / меньше, без знака). Эти условные операции основаны на состоянии определенных битов в регистре (E) FLAGS . Многие арифметические и логические операции устанавливают, очищают или дополняют эти флаги в зависимости от их результата. Сравнение cmp(сравнение) и testинструкции устанавливают флаги, как если бы они выполнили вычитание или побитовую операцию И, соответственно, без изменения значений операндов. Также есть такие инструкции, как clc(очистить флаг переноса) иcmc(дополнительный флаг переноса), которые работают непосредственно с флагами. Сравнение с плавающей запятой выполняется с помощью инструкций fcomили, ficomкоторые в конечном итоге должны быть преобразованы в целочисленные флаги.

Каждая операция перехода имеет три различных формы, в зависимости от размера операнда. Короткий прыжок использует 8-разрядное операнд, который представляет собой относительное смещение от текущей инструкции. Рядом прыжок похож на короткий прыжок , но использует 16-битный знаковый операнд (в реальном или защищенном режиме) или 32-разрядные знаковый операнд (в защищенном режиме 32-битного только). Далеко скачок является тот , который использует полную базу сегмента: значение смещения в качестве абсолютного адреса. Существуют также косвенные и индексированные формы каждого из них.

Помимо простых операций перехода, существуют инструкции call(вызов подпрограммы) и ret(возврат из подпрограммы). Перед передачей управления подпрограмме callпомещает адрес смещения сегмента инструкции, следующей за callфункцией, в стек; retизвлекает это значение из стека и переходит к нему, эффективно возвращая поток управления этой части программы. В случае a far callоснование сегмента сдвигается вслед за смещением; far retвыталкивает смещение, а затем базу сегмента для возврата.

Есть также две похожие инструкции int( прерывание ), которые сохраняют текущее значение регистра (E) FLAGS в стеке, а затем выполняют a far call, за исключением того, что вместо адреса он использует вектор прерывания , индекс в таблице обработчика прерываний. адреса. Обычно обработчик прерывания сохраняет все другие регистры ЦП, которые он использует, если только они не используются для возврата результата операции в вызывающую программу (в программном обеспечении, называемом прерываниями). Соответствующая инструкция возврата из прерывания - это iret, которая восстанавливает флаги после возврата. Мягкие прерывания описанного выше типа используются некоторыми операционными системами для системных вызовов., а также может использоваться при отладке обработчиков аппаратных прерываний. Аппаратные прерывания запускаются внешними аппаратными событиями и должны сохранять все значения регистров, поскольку состояние выполняющейся в данный момент программы неизвестно. В защищенном режиме операционная система может настроить прерывания для запуска переключения задач, которое автоматически сохранит все регистры активной задачи.

Примеры [ править ]

"Привет мир!" программа для DOS на ассемблере в стиле MASM [ править ]

Использование прерывания 21h для вывода - в других примерах используется printf библиотеки libc для вывода на стандартный вывод . [10]

.model  small .stack  100h.data msg db 'Hello world! $'. начало кода : mov ah ,  09h  ; Вывести сообщение: lea dx ,  msg int 21h mov ax ,  4C00h  ; Завершить исполняемый файл int 21hконец  начало

"Привет мир!" программа для Windows в стиле сборки MASM [ править ]

; требуется переключатель / coff в 6.15 и более ранних версиях .386 .model  small , c .stack  1000h.data msg  db  "Hello world!" , 0.code includelib  libcmt.lib includelib  libvcruntime.lib includelib  libucrt.lib includelib  legacy_stdio_definitions.libextrn  printf : рядом внешний  выход : рядомpublic  main main  proc  push  offset  msg  call  printf  push  0  call  exit main  endpконец

"Привет мир!" программа для Windows в стиле сборки NASM [ править ]

; База изображения = 0x00400000 % define RVA (x) (x-0x00400000) раздел .text push  dword  hello call  dword  [ printf ] push  byte  + 0 call  dword  [ exit ] retраздел .data hello  db  "Hello world!"раздел .idata дд  ООА ( msvcrt_LookupTable ) дд  - 1 дд  0 дд  ООА ( msvcrt_string ) дд  ООА ( msvcrt_imports ) раз  5  дд  0  ; заканчивает таблицу дескрипторовmsvcrt_string  dd  "msvcrt.dll" ,  0 msvcrt_LookupTable: dd  RVA ( msvcrt_printf ) dd  RVA ( msvcrt_exit ) dd  0msvcrt_imports: printf  dd  RVA ( msvcrt_printf ) exit  dd  RVA ( msvcrt_exit ) dd  0msvcrt_printf: dw  1 dw  "printf" ,  0 msvcrt_exit: dw  2 dw  "exit" ,  0 dd  0

"Привет мир!" программа для Linux на сборке в стиле NASM [ править ]

; ; Эта программа работает в 32-битном защищенном режиме. ; сборка: nasm -f elf -F stabs name.asm ; ссылка: ld -o name name.o ; ; В длинном 64-битном режиме вы можете использовать 64-битные регистры (например, rax вместо eax, rbx вместо ebx и т. Д.) ; Также измените "-f elf" на "-f elf64" в команде сборки. ; раздел .data  ; раздел для инициализированных данных str:  db  'Hello world!' ,  0Ah  ; Строка сообщения с новой строкой полукоксом в конце (10 десятичных) str_len:  фас  $  -  ул  ; вычисляет длину строки (в байтах) путем вычитания начального адреса строки  ; с этого адреса (символ $)раздел .text  ; это глобальный раздел кода _start  ; _start - это точка входа и требует, чтобы глобальная область видимости была «видна»  ; линкер --equivalent к основному () в C / C ++ _start:  ; здесь начинается определение процедуры _start mov eax ,  4  ; указать код функции sys_write (из таблицы векторов ОС) mov ebx ,  1  ; укажите дескриптор файла stdout - в gnu / linux все обрабатывается как файл  ,; даже аппаратные устройства mov ecx ,  str  ; переместить начальный _адрес_ строкового сообщения в регистр ecx mov edx ,  str_len ; длина перемещения сообщения (в байтах) int 80h  ; прерывание ядра для выполнения только что настроенного системного вызова -  ; в gnu / linux службы запрашиваются через ядро mov eax ,  1  ; указать код функции sys_exit (из таблицы векторов ОС) mov ebx ,  0  ; указать код возврата для ОС (ноль означает, что ОС все прошло нормально) int 80h  ; прерывание ядра для выполнения системного вызова (для выхода)

"Привет мир!" программа для Linux в стиле сборки NASM с использованием стандартной библиотеки C [ править ]

; ; Эта программа работает в 32-битном защищенном режиме. ; gcc по умолчанию связывает стандартную библиотеку C; сборка: nasm -f elf -F stabs name.asm ; ссылка: gcc -o name name.o ; ; В длинном 64-битном режиме вы можете использовать 64-битные регистры (например, rax вместо eax, rbx вместо ebx и т. Д.) ; Также измените "-f elf" на "-f elf64" в команде сборки. ;  global main  ; main должен быть определен, поскольку он компилируется в C-стандартной библиотеке  extern printf  ; объявляет использование внешнего символа, поскольку printf объявлен в другом объектном модуле.  ; Компоновщик разрешит этот символ позже.сегмент .data  ; секция для инициализированной строки  данных db  'Hello world!' ,  0Ah ,  0h  ; строка сообщения с символом новой строки (10 десятичных) и  символом конца NULL ; теперь строка относится к начальному адресу, по которому хранится «Hello, World».сегмент .text main:  push  string  ; поместить адрес первого символа строки в стек. Это будет аргумент для  вызова  printf printf  ; вызывает printf  add  esp ,  4  ; продвигает указатель стека на 4, сбрасывая переданный строковый аргумент  ret  ; return

"Привет мир!" программа для 64-битного режима Linux на сборке в стиле NASM [ править ]

; build: nasm -f elf64 -F dwarf hello.asm ; ссылка: ld -o hello hello.oDEFAULT  REL ; использовать режимы относительной адресации RIP по умолчанию, поэтому [foo] = [rel foo]РАЗДЕЛ .rodata ; данные только для чтения могут находиться в разделе .rodata в GNU / Linux, например .rdata в Windows Hello: db  "Hello world!" , 10  ; 10 = `\ n`. len_Hello: фас  $ - Привет  ; заставить NASM вычислить длину как постоянную времени сборки ;; write () принимает длину, поэтому строка в стиле C с завершающим нулем не требуется. Это было бы для путРАЗДЕЛ. Текстглобальный _start _start: mov  eax ,  1 ; __NR_write номер системного вызова из Linux asm / unistd_64.h (x86_64) mov  edi ,  1 ; int fd = STDOUT_FILENO lea  rsi ,  [ rel  Hello ] ; x86-64 использует RIP-относительный LEA поставить статические адреса в REGS Mov  RDX ,  len_Hello ; size_t count = len_Hello системный вызов ; написать (1, Привет, len_Hello); вызов ядра, чтобы фактически выполнить системный вызов  ;; возвращаемое значение в RAX. RCX и R11 также перезаписываются системным вызовомmov  eax ,  60 ; __NR_exit номер вызова (x86_64) xor  edi ,  edi ; status = 0 (нормально выйти) системный вызов ; _exit (0)

Запуск его под straceпроверяет, что в процессе не выполняется никаких дополнительных системных вызовов. Версия printf будет делать гораздо больше системных вызовов для инициализации libc и динамического связывания . Но это статический исполняемый файл, потому что мы скомпоновали его с помощью ld без -pie или каких-либо разделяемых библиотек; единственные инструкции, которые выполняются в пользовательском пространстве, - это те, которые вы предоставляете.

$ strace ./hello> / dev / null # без перенаправления stdout вашей программы - это смешанная регистрация strace на stderr. Обычно это нормально execve ("./ hello", ["./hello"], 0x7ffc8b0b3570 / * 51 vars * /) = 0 write (1, "Hello world! \ N", 13) = 13 exit (0) знак равно +++ завершился с 0 +++

Использование регистра флагов [ править ]

Флаги широко используются для сравнений в архитектуре x86. При сравнении двух данных ЦП устанавливает соответствующий флаг или флаги. После этого можно использовать инструкции условного перехода для проверки флагов и перехода к коду, который должен выполняться, например:

cmp eax ,  ebx jne do_something ; ... do_something: ; сделай что-нибудь здесь

Флаги также используются в архитектуре x86 для включения и выключения определенных функций или режимов выполнения. Например, чтобы отключить все маскируемые прерывания, вы можете использовать инструкцию:

cli

К регистру флагов также можно получить прямой доступ. Младшие 8 бит регистра флага могут быть загружены с ahпомощью lahfинструкции. Весь регистр флагов также могут быть перемещены и выключать стек , используя инструкции pushf, popf, int( в том числе into) и iret.

Использование регистра указателя инструкций [ править ]

Указатель команд вызывается ipв 16-битном режиме, eipв 32-битном режиме, так и ripв 64-битном режиме. Регистр указателя команд указывает на адрес памяти, который процессор попытается выполнить в следующий раз; к нему нельзя получить прямой доступ в 16-битном или 32-битном режиме, но можно записать последовательность, подобную следующей, для помещения адреса next_lineв eax:

вызов next_line next_line: pop eax

Эта последовательность инструкций генерирует независимый от позиции код, поскольку callпринимает непосредственный операнд, относящийся к указателю инструкции, описывающий смещение в байтах целевой инструкции от следующей инструкции (в данном случае 0).

Запись в указатель инструкции проста - jmpинструкция устанавливает указатель инструкции на целевой адрес, поэтому, например, последовательность, подобная следующей, поместит содержимое eaxв eip:

jmp eax

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

См. Также [ править ]

  • язык ассемблера
  • Списки инструкций X86
  • Архитектура X86
  • Конструкция процессора
  • Список сборщиков
  • Самомодифицирующийся код
  • ДОС

Ссылки [ править ]

  1. ^ "Семейство микропроцессоров Intel 8008 (i8008)" . www.cpu-world.com . Проверено 25 марта 2021 .
  2. ^ "Intel 8008" . CPU MUSEUM - МУЗЕЙ МИКРОПРОЦЕССОРОВ И ФОТОГРАФИИ УМЕРЫ . Проверено 25 марта 2021 .
  3. ^ a b c "Intel 8008 OPCODES" . www.pastraiser.com . Проверено 25 марта 2021 .
  4. ^ a b c d e Нараям, Рам (2007-10-17). «Ассемблеры Linux: сравнение GAS и NASM» . Архивировано из оригинала 3 октября 2013 года . Проверено 2 июля 2008 .
  5. ^ «Создание Unix» . Архивировано из оригинального 2 -го апреля 2014 года.
  6. ^ Хайд, Рэндалл. "Какой ассемблер лучший?" . Проверено 18 мая 2008 .
  7. ^ «Новости GNU Assembler, v2.1 поддерживает синтаксис Intel» . 2008-04-04 . Проверено 2 июля 2008 .
  8. ^ «i386-Ошибки (Использование как)» . Документация Binutils . Проверено 15 января 2020 года .
  9. Мюллер, Скотт (24 марта 2006 г.). «P2 (286) Процессоры второго поколения» . Обновление и ремонт ПК, 17-е издание (книга) (17-е изд.). Que. ISBN  0-7897-3404-4. Проверено 6 декабря 2017 .
  10. ^ "Я только начал сборку" . daniweb.com . 2008 г.

Дальнейшее чтение [ править ]

Руководства [ править ]

  • Руководства для разработчиков программного обеспечения Intel 64 и IA-32
  • Руководство программиста по архитектуре AMD64 (том 1-5)

Книги [ править ]

  • Эд, Йоргенсен (май 2018 г.). Программирование на языке ассемблера x86-64 с Ubuntu (PDF) (редакция 1.0.97). п. 367.
  • Денис Юричев: понимание языка ассемблера

Внешние ссылки [ править ]