Язык ассемблера X86 - X86 assembly language

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

Мнемоника и коды операций

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

Синтаксис

ассемблер x86 имеет два основных синтаксис ветви: Intel синтаксис, изначально использовавшийся для документации платформа x86, и AT&T синтаксис.[1] Синтаксис Intel доминирует в ДОС и Windows мире, а синтаксис AT&T доминирует в Unix мире, поскольку Unix была создана в AT&T Bell Labs.[2]Вот краткое изложение основных различий между Синтаксис Intel и Синтаксис AT&T:

AT&TIntel
Порядок параметровИсточник перед местом назначения.
движение $5, % eax
Место назначения перед источником.
mov eax, 5
Размер параметраМнемоника имеет суффикс с буквой, обозначающей размер операндов: q для qword, л надолго (dword), ш на слово, и б для байта.[1]
добавить $4, % esp
Производный от имени используемого регистра (например, rax, eax, ax, al подразумевать д, л, ш, б, соответственно).
Добавить особенно, 4
СигилыБлижайшие значения с префиксом «$», регистры с префиксом «%».[1]Ассемблер автоматически определяет тип символов; то есть регистры, константы или что-то еще.
Эффективный адресаОбщий синтаксис DISP (БАЗА, ИНДЕКС, МАСШТАБ). Пример:
движение mem_location(% ebx,% ecx,4), % eax
Арифметические выражения в квадратных скобках; кроме того, ключевые слова размера, такие как байт, слово, или же dword должны использоваться, если размер не может быть определен по операндам.[1] Пример:
mov eax, [ebx + ecx*4 + mem_location]

Многие ассемблеры x86 используют Синтаксис Intel, включая NASM, FASM, MASM, ТАСМ, и YASM. ГАЗ, который изначально использовал Синтаксис AT&T, поддерживает оба синтаксиса с версии 2.10 через .intel_syntax директива.[1][3][4] Особенность синтаксиса AT&T для x86 заключается в том, что операнды x87 перевернуты, что является унаследованной ошибкой от исходного ассемблера AT&T.[5]


Регистры

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

  • 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 топор, 1234h ; копирует значение 1234hex (4660d) в регистр AX
mov bx, топор    ; копирует значение регистра AX в регистр BX

Сегментированная адресация

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

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

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

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

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

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

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

  • CS: IP (CS - это Сегмент кода, IP Указатель инструкции) указывает на адрес, по которому процессор будет извлекать следующий байт кода.
  • СС: СП (СС есть Сегмент стека, 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, Реальный режим, Защищенный режим, Длинный режим, Виртуальный режим 86, и Режим управления системой, в котором одни инструкции доступны, а другие нет. 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-битный защищенный режим (используется в конце ДОС эпохи) использовала сложную многосегментную модель памяти. В 32-битном защищенном режиме используется простая плоская модель памяти.
  • Длинный режим (64-битный)
    • В основном это расширение набора 32-битных (защищенный режим) инструкций, но в отличие от перехода с 16 на 32 бит, многие инструкции были отброшены в 64-битном режиме. Первопроходец AMD.
  • Виртуальный режим 8086 (16 бит)
    • Специальный гибридный режим работы, который позволяет запускать программы и операционные системы в реальном режиме под управлением операционной системы супервизора защищенного режима.
  • Режим управления системой (16 бит)
    • Обрабатывает общесистемные функции, такие как управление питанием, управление системным оборудованием и собственный код, разработанный OEM. Он предназначен для использования только системной прошивкой. Все нормальное исполнение, включая Операционная система, приостановлено. Альтернативная программная система (которая обычно находится в компьютерном прошивка, или аппаратное обеспечение отладчик ) затем выполняется с высокими привилегиями.

Режимы переключения

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

Примеры

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

С работающим компьютером UEFI, прошивка UEFI (кроме CSM и устаревших Дополнительное ПЗУ ), UEFI загрузчик а ядро ​​операционной системы UEFI все работает в длинном режиме.

Типы инструкций

В целом черты современного набор инструкций x86 находятся:

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

Инструкции по стеку

Архитектура x86 имеет аппаратную поддержку механизма стека выполнения. Такие инструкции, как толкать, поп, вызов и Ret используются с правильно настроенным стеком для передачи параметров, выделения места для локальных данных, а также для сохранения и восстановления точек возврата вызова. В Ret размер инструкция очень полезна для реализации эффективного (и быстрого) использования пространства соглашения о вызовах где вызываемый объект отвечает за освобождение пространства стека, занятого параметрами.

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

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

Целочисленные инструкции ALU

сборка x86 имеет стандартные математические операции, Добавить, суб, мул, с идив; в логические операторы и, или же, xor, негр; битовый сдвиг арифметические и логические, сал/сар, shl/шр; вращаться с переносом и без него, rcl/rcr, ролл/рор, дополнение BCD арифметические инструкции, ааа, аад, даа и другие.

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

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

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

Инструкции SIMD

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

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

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

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

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

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

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

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

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

 войти   4, 0

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

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

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

Ход программы

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

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

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

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

Также есть две похожие инструкции, int (прерывать ), что сохраняет текущий (E) ФЛАГИ значение регистра в стеке, затем выполняет дальний звонок, за исключением того, что вместо адреса используется вектор прерывания, индекс в таблице адресов обработчиков прерываний. Обычно обработчик прерывания сохраняет все другие регистры ЦП, которые он использует, если только они не используются для возврата результата операции в вызывающую программу (в программном обеспечении, называемом прерываниями). Соответствующий возврат из инструкции прерывания ирет, который восстанавливает флаги после возврата. Мягкие прерывания описанного выше типа используются некоторыми операционными системами для системные вызовы, а также может использоваться при отладке обработчиков аппаратных прерываний. Жесткие прерывания запускаются внешними аппаратными событиями и должны сохранять все значения регистров, поскольку состояние выполняющейся в данный момент программы неизвестно. В защищенном режиме операционная система может настроить прерывания для запуска переключения задач, которое автоматически сохранит все регистры активной задачи.

Примеры

"Привет, мир!" программа для DOS в стиле сборки MASM

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

.модель маленький.куча 100ч.данныесообщение	db	'Привет, мир! $'.кодНачните:	mov	ах, 09ч   ; Показать сообщение	леа	dx, сообщение	int	21ч	mov	топор, 4C00h  ; Завершить исполняемый файл	int	21чконец Начните

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

; требуется переключатель / coff в 6.15 и более ранних версиях.386.модель маленький,c.куча 1000ч.данныесообщение     db "Привет, мир!",0.кодincludelib libcmt.libincludelib libvcruntime.libincludelib libucrt.libincludelib legacy_stdio_definitions.libвнешний printf:возлевнешний выход:возлеобщественный главныйглавный proc        толкать    компенсировать сообщение        вызов    printf        толкать    0        вызов    выходглавный конецконец

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

; База изображения = 0x00400000% определить RVA (x) (x-0x00400000)раздел .тексттолкать dword Приветвызов dword [printf]толкать байт +0вызов dword [выход]Retраздел .данныеПривет db "Привет, мир!"раздел .idataдд RVA(msvcrt_LookupTable)дд -1дд 0дд RVA(msvcrt_string)дд RVA(msvcrt_imports)раз 5 дд 0 ; заканчивает таблицу дескрипторовmsvcrt_string дд "msvcrt.dll", 0msvcrt_LookupTable:дд RVA(msvcrt_printf)дд RVA(msvcrt_exit)дд 0msvcrt_imports:printf дд RVA(msvcrt_printf)выход дд RVA(msvcrt_exit)дд 0msvcrt_printf:dw 1dw "printf", 0msvcrt_exit:dw 2dw "выход", 0дд 0

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

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

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

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

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

; сборка: nasm -f elf64 -F карлик hello.asm; ссылка: ld -o hello hello.oДЕФОЛТ REL			; по умолчанию использовать режимы относительной адресации RIP, поэтому [foo] = [rel foo]РАЗДЕЛ .rodata			; данные только для чтения могут находиться в разделе .rodata в GNU / Linux, как .rdata в WindowsПривет:		db "Привет, мир!",10        ; 10 = ` n`.len_Hello:	равно $-Привет                 ; заставить NASM рассчитать длину как постоянную времени сборки;; write () принимает длину, поэтому строка в стиле C с завершением 0 не требуется. Было бы для путРАЗДЕЛ .текстГлобальный _Начните_Начните:	mov eax, 1				; __NR_write номер системного вызова из Linux asm / unistd_64.h (x86_64)	mov Edi, 1				; интервал fd = STDOUT_FILENO	леа RSI, [rel Привет]			; x86-64 использует относительный RIP LEA для помещения статических адресов в регистры	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 вар * /) = 0write (1, «Привет, мир!  n», 13) = 13выход (0) =?+++ завершился с 0 +++

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

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

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

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

	cli

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

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

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

	вызов	next_lineследующая_строка:	поп	eax

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

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

	jmp	eax

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

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

Рекомендации

  1. ^ а б c d е Нараям, Рам (2007-10-17). «Ассемблеры Linux: сравнение GAS и NASM». Архивировано из оригинал 3 октября 2013 г.. Получено 2008-07-02.
  2. ^ «Создание Unix». Архивировано из оригинал 2 апреля 2014 г.
  3. ^ Хайд, Рэндалл. "Какой ассемблер лучший?". Получено 2008-05-18.
  4. ^ «Новости GNU Assembler, v2.1 поддерживает синтаксис Intel». 2008-04-04. Получено 2008-07-02.
  5. ^ "i386-Ошибки (Использование как)". Документация Binutils. Получено 15 января 2020.
  6. ^ Мюллер, Скотт (24 марта 2006 г.). «P2 (286) Процессоры второго поколения». Обновление и ремонт ПК, 17-е издание (Книга) (17-е изд.). Que. ISBN  0-7897-3404-4. Получено 2017-12-06.
  7. ^ "Я только начал сборку". daniweb.com. 2008.

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

Руководства

Книги

внешняя ссылка