Программы для программирования на ассемблере
Обновлено: 21.11.2024
Вначале были перфокарты. В конце концов, кому-то пришла в голову блестящая идея сделать компьютер программируемым. Просто введите шестнадцатеричный код и запустите его. Проблема в том, что очень сложно посмотреть на шестнадцатеричный код и понять, что он делает.
Войти в сборку
Сборка по-прежнему сведена к минимуму, и каждая деталь того, как компьютер выполняет свою задачу, должна быть указана. Разница в том, что Assembly делает эти инструкции удобочитаемыми для человека.
Следующим шагом будет использование языка программирования, такого как C, Java или Typescript. Это, безусловно, проще, чем использование ассемблера, но и по сей день все еще существуют задачи, которые не могут решить языки системного программирования. Вот некоторые примеры:
- Агрессивная оптимизация (C и Rust уже очень быстры, но не идеальны)
- Assembly упрощает расчет времени выполнения программы.
- Программы, которые должны работать напрямую с оборудованием, например драйверы.
- Загрузка операционной системы
Требования
К сожалению, сборка не одинакова во всех системах. Разным компьютерам для работы нужен разный код. Вот что вам нужно для этого урока:
- Компьютер x86 (например, это не будет работать на Raspberry Pi)
- 32-разрядная или 64-разрядная операционная система (предпочтительно Linux)
- Ассемблер (NASM в Linux или MASM в Windows)
- Опыт программирования на низком уровне (C, C++, Rust и Go — хорошие языки для знания)
Разделы
- текст. Этот раздел содержит фактические инструкции, которые будет выполнять ваш код.
- bss — здесь хранятся все глобальные переменные. Сюда помещается любая статическая переменная.
- data — этот раздел используется для постоянных глобальных переменных.
Секции объявляются простым вводом section .name . Например, раздел данных будет объявлен с использованием:
Переменные
Переменные, как мы уже говорили, хранятся в разделе bss. Мы не можем просто объявить их значение, как в обычном языке. Вместо этого мы можем точно указать ассемблеру, сколько байтов нужно зарезервировать.
Это создает переменную с именем var и резервирует для нее четыре байта. Если бы мы хотели зарезервировать два байта, мы бы поставили 2 в конце. Чтобы получить доступ к значению var , мы заключаем его имя в квадратные скобки: [var] .
Утверждения
Выражение в ассемблере имеет следующий формат:
Давайте разберемся.
Мнемоника — это то, что нужно запустить. Некоторые операции принимают один параметр. Некоторые берут несколько. В ассемблере много инструкций, но мы сосредоточимся на следующих.
Мнемоника | Операнд 1 | Операнд 2 | Описание |
---|---|---|---|
mov | местоположение< /td> | value | Устанавливает операнд 1 в операнд 2 |
inc | location | Добавляет единицу к местоположению | |
dec | location | Вычитает единицу из местоположения | |
добавить | местоположение | значение | Добавляет значение в местоположение | tr>
sub | местоположение | значение | Вычитает значение из местоположения |
jmp | метка | Переход к части программы | |
cmp | value1 | value2 | Сравнивает два значения |
je | label | Переход к части программы, если два значения равны | |
int | interrupt | Создает программное прерывание |
Комментарии в ассемблере — это все, что идет после точки с запятой ( ; ). Вы уже должны быть знакомы с тем, что они делают — они помогают объяснить ваш код другим людям, которые его читают.
Мы подробнее остановимся на этих инструкциях позже. А пока вот несколько примеров:
Ярлыки
Рассмотрите следующий код C
В этом коде используется цикл while для бесконечного повторения. Однако в сборке нет таких простых циклов. В сборке вам нужно сделать что-то более похожее на следующее
Простите, если вы не знаете, что это допустимый код C. (Это довольно плохая практика.) Но в ассемблере это все, что у вас есть. Давайте попробуем перевести это на Ассемблер.
Давайте настроим нашу программу. Нам нужен текстовый раздел для хранения программных инструкций и раздел bss для хранения нашей переменной.
Мы еще не говорили об этом, но нам нужно сообщить программе, с чего начать в нашей программе. Мы создадим метку с именем _start и начнем с нее. Мы можем указать компоновщику, с чего начать, используя глобальный _start .
Теперь нам нужно создать нашу переменную. Мы будем использовать 32-битное целое число, для которого требуется четыре байта.
Теперь нам нужно инициализировать переменную. Именно для этого предназначена инструкция mov.
Теперь нам нужен цикл. Мы создадим метку, назовем ее циклом и безоговорочно перейдем к ней.
Наконец, нам нужно увеличить нашу переменную.
Возможно, мне следует упомянуть, как вы можете запустить это. Предположим, что файл называется incrementor.asm и вы используете NASM:
Регистры
Знаете ли вы, что ваш ЦП имеет встроенную память? 😲 Регистры — это память, встроенная в ЦП. Благодаря этому можно молниеносно использовать регистры вместо хранения значений в оперативной памяти.
Так почему бы нам просто не использовать регистры для всего? Вот в чем проблема. У нас не так много регистров. В этом уроке будут использоваться только четыре. Это станет проблемой позже, но пока нам нужно меньше четырех переменных, это должно работать для нас. Мы будем использовать четыре: eax, ebx, ecx и edx. Мы будем использовать эти четыре, потому что их очень легко запомнить. Все они следуют формату e_x . Каждый из этих регистров может хранить одно 32-битное число.
Мы можем переписать наш бесконечный цикл, чтобы использовать регистр
Теперь нам вообще не нужна оперативная память!… кроме как для хранения фактической программы в памяти. Нам также не нужно указывать размер операции. Размер eax всегда равен четырем байтам.
Заключение
На этом основы ассемблера заканчиваются. Прочтите мою следующую статью о том, как написать настоящую программу на ассемблере.
В предыдущей статье мы рассмотрели общие инструкции x86, которые можно использовать при написании программ на ассемблере. Благодаря полученным знаниям мы можем приступить к написанию нашей первой программы на ассемблере.
В этой статье представлен обзор того, как создать программу и выполнить приложение, полностью построенное на ассемблере x86.
Ввод и вывод: системные вызовы x86
Операционные системы содержат подпрограммы для выполнения различных низкоуровневых операций. Если мы хотим вызвать эти подпрограммы операционной системы из нашей программы, нам нужно вызвать системные вызовы. Системный вызов — это мост между пользовательской программой и подпрограммой операционной системы. Если мы хотим записать строку в консоль вывода, вместо того, чтобы каждый раз писать подпрограмму с нуля, мы можем использовать подпрограмму, которая уже существует в операционной системе. Этого можно добиться с помощью системного вызова.
Согласно Википедии, «системный вызов — это то, как программа запрашивает службу у ядра операционной системы. Сюда могут входить службы, связанные с оборудованием (например, доступ к жесткому диску), создание и выполнение новых процессов и взаимодействие со встроенными службами ядра (например, планирование). Системные вызовы обеспечивают необходимый интерфейс между процессом и операционной системой».
В сборке Ubuntu 20.04 Desktop x64 мы можем просмотреть следующий файл, чтобы просмотреть полный список системных вызовов x86 и связанных с ними доступных номеров системных вызовов.
/usr/include/x86_64-linux-gnu/asm/unistd_32.h |
Теперь давайте извлечем некоторые системные вызовы и связанные с ними номера системных вызовов.
В следующем фрагменте показан системный вызов READ, который можно использовать для получения пользовательского ввода.
Как показано, системный вызов READ имеет номер системного вызова 3. Аналогично, в следующем отрывке показан системный вызов WRITE, который можно использовать для записи вывода на консоль.
Как было отмечено выше, системный вызов записи имеет номер системного вызова 4. Если мы хотим использовать эти системные вызовы в наших программах на ассемблере x86, мы должны использовать их соответствующие номера.
Аналогично следующий фрагмент показывает системный вызов выхода.
Как мы видим, системный вызов exit имеет номер системного вызова 1.
Привет, мир! Создание обычного Hello World в x86
Теперь, когда мы понимаем, что такое системные вызовы и как можно найти номера системных вызовов, давайте напишем простую программу на ассемблере x86 для вывода строки Hello World!
Следует отметить, что мы будем использовать системный вызов записи для вывода строки Hello World! Чтобы лучше понять аргументы и другие данные, которые требуются этому системному вызову, мы можем прочитать справочную страницу, как показано в следующей команде.
Ниже приведен отрывок из вывода предыдущей команды.
ssize_t write(int fd, const void *buf, size_t count); |
Функция записи требует 3 аргумента. Первый аргумент — это дескриптор файла, который в данном случае является стандартным выводом и принимает значение 1. Второй аргумент — это константный буфер, который является указателем на сообщение, которое мы хотим напечатать. Третий аргумент — это длина строки.При вызове системных вызовов нам нужно будет указать эти значения в соответствующих регистрах. Ниже приведен стандартный шаблон, которому мы должны следовать при написании программ на ассемблере в x86.
Первый аргумент входит в EBX; второй аргумент входит в ECX; третий аргумент входит в регистр EDX. Номер системного вызова заносится в регистр EAX.
С учетом всех этих деталей ниже представлена программа, которая печатает строку Hello, world! в выходную консоль.
msg db ‘Привет, мир!’,0xa
В программе есть два раздела: .text и .rodata. В разделе .rodata есть строка, определенная с помощью метки msg. Для процедуры записи также требуется длина строки, поэтому мы вычисляем длину строки без жесткого кодирования и сохраняем ее в метке len.
В разделе .text мы использовали глобальную директиву _start для указания точки входа в программу. В точке входа мы помещаем значение 1 для стандартного вывода в EBX, указатель на строку помещается в ECX, а длина строки помещается в EDX. Наконец, мы поместили системный вызов номер 4 в регистр EAX. Чтобы вызвать системный вызов, мы выполнили инструкцию int 0x80.
Затем мы можем собрать и скомпоновать программу, используя следующие команды:
ld helloworld.o -o helloworld -m elf_i386
После этого мы можем запустить программу, как показано ниже.
Ошибка сегментации (дамп ядра)
Как видно из предыдущего вывода, строка Hello, world! печатается. Однако мы также должны заметить ошибку сегментации. Подробнее об ошибках сегментации и о том, как определить их причины, мы поговорим в следующей статье.
Строки/ASCII: как работать со строками и ASCII в x86
В этом разделе давайте расширим наш предыдущий Hello, world! программа, запрашивающая ввод у пользователя, а затем печатающая введенный текст обратно на экран.
Язык ассемблера — это язык программирования низкого уровня. Это помогает понять язык программирования для машинного кода. В компьютерах есть ассемблер, который помогает преобразовать ассемблерный код в исполняемый машинный код. Язык ассемблера предназначен для понимания инструкций и предоставления их машинному языку для дальнейшей обработки. В основном это зависит от архитектуры системы, будь то операционная система или архитектура компьютера.
Язык ассемблера в основном состоит из мнемонических инструкций процессора или данных и других утверждений или инструкций. Он создается с помощью компиляции исходного кода языка высокого уровня, такого как C, C++. Язык ассемблера помогает в тонкой настройке программы.
Веб-разработка, языки программирования, тестирование программного обеспечения и другое
Почему полезен язык ассемблера?
Язык ассемблера помогает программистам писать удобочитаемый код, почти аналогичный машинному языку. Машинный язык сложен для понимания и чтения, так как это всего лишь последовательность чисел. Язык ассемблера помогает обеспечить полный контроль над задачами, которые выполняет компьютер.
Пример:
Выполните следующие действия, чтобы напечатать «Hello world» в Windows
global _main
extern _printf
section .text
_main:
push message
call _printf
add esp, 4
ret < br />сообщение:
db 'Привет, мир!', 10, 0
- Сохраните файл под любым именем, например XYZ.asm; расширение должно быть «.asm».
- Приведенный выше файл необходимо скомпилировать с помощью ассемблера NASM (Netwide Assembler).
- Выполните команду nasm –f win32 XYZ.asm
- После этого Nasm создает один объектный файл, содержащий машинный код, но не исполняемый код, который называется XYZ.obj.
- Для создания исполняемого файла для Windows используется Minimal GNU, который предоставляет компилятор GCC.
- Выполните команду gcc –o XYZ.exe XYZ.obj
- Выполнить исполняемый файл теперь «XYZ».
- Вывод будет выглядеть как "Привет, мир".
Зачем вам изучать язык ассемблера?
Изучение языка ассемблера по-прежнему важно для программистов. Это помогает получить полный контроль над системой и ее ресурсами. Изучив язык ассемблера, программист может написать код для доступа к регистрам и получить адрес памяти указателей и значений. В основном это помогает оптимизировать скорость, что повышает эффективность и производительность.
Изучение языка ассемблера помогает понять функции процессора и памяти. Если программист пишет любую программу, которая должна быть компилятором, это означает, что программист должен иметь полное представление о процессоре. Язык ассемблера помогает в понимании работы процессоров и памяти. Это загадочный и символический язык.
Все в одном пакете для разработки программного обеспечения (600+ курсов, 50+ проектов) 600+ онлайн-курсов | 3000+ часов | Поддающиеся проверке сертификаты | Пожизненный доступ
4,6 (3144 оценки)
Язык ассемблера помогает напрямую обращаться к оборудованию. Этот язык в основном основан на компьютерной архитектуре, и он распознает определенный тип процессора и отличается для разных процессоров. Язык ассемблера относится к прозрачности по сравнению с другими языками высокого уровня. Он имеет небольшое количество операций, но помогает понять алгоритмы и другие элементы управления потоком. Это делает код менее сложным и упрощает отладку.
Возможности
Особенности языка ассемблера упомянуты ниже:
- Он может использовать не числовой, а мнемонический код операции, а также предоставляет информацию о любой ошибке в коде.
- Этот язык помогает в указании символического операнда, что означает, что ему не нужно указывать машинный адрес этого операнда. Его можно представить в виде символа.
- Данные могут быть объявлены с использованием десятичной записи.
Ассемблеры
- Однопроходный ассемблер: Один проход ассемблера называется полным сканированием входных данных исходной программы на ассемблере или эквивалентным представлением и трансляцией оператором на основе оператора, который называется однопроходным ассемблером или однопроходным переводом. Он изолирует метку, мнемонику и поле операнда системы. Он проверяет кодовые инструкции, просматривая их в таблице мнемонических кодов. Он вводит символ, найденный в поле метки, и адресует доступное машинное слово текста в таблицу символов. Этот проход быстрый и эффективный, и нет необходимости создавать промежуточный код.
- Многопроходный ассемблер. В этом случае ассемблер несколько раз проходит язык ассемблера и генерирует объектный код. Этот последний проход называется проходом синтеза, и этот ассемблер требует любой формы промежуточного кода для генерации каждого прохода каждый раз. Он сравнительно медленнее, чем однопроходный ассемблер, но некоторые действия, которые можно выполнять более одного раза, означают дублирование.
Преимущества и недостатки
Упомянуты некоторые преимущества и недостатки:
Преимущества
Ниже приведены преимущества:
- Это упрощает выполнение сложных задач.
- Эффективно использует память, так как требует меньше памяти.
- Он быстрее по скорости, так как время его выполнения меньше.
- Оно в основном ориентировано на аппаратное обеспечение.
- Для получения результата требуется меньше инструкций.
- Он используется для критически важных задач.
- Не требуется отслеживать ячейки памяти.
- Это встроенная система низкого уровня.
Недостатки
Ниже перечислены недостатки:
- Написание кода для этого требует много времени и усилий.
- Это очень сложно и трудно понять.
- Синтаксис трудно запомнить.
- Ему не хватает переносимости программы между компьютерами разных архитектур.
- Для запуска длинных программ, написанных на языке ассемблера, требуется больше места или памяти компьютера.
Заключение
Язык ассемблера очень важен для понимания компьютерной архитектуры и программ для программистов. Программисты в основном использовали многие другие языки программирования для разработки приложений и программного обеспечения, но язык ассемблера также важен. Это помогает программистам многого добиться, если они реализуют язык ассемблера. Сборки содержат много метаданных, таких как номер версии, сведения о локализации и другие сведения о продукте. Это важная часть, которая предоставляется пользователю после цифровой подписи.
Если человек хочет знать, как работает система и процессор, то язык ассемблера — это тот, который решает эту задачу. Это помогает во всех аспектах, от понимания алгоритма программы до работы процессора и регистрации регистров компьютера. Выбор языка для продолжения зависит от индивидуального выбора.
Рекомендуемая статья
Это руководство о том, что такое язык ассемблера. Здесь мы обсудили особенности, преимущества и недостатки языка ассемблера. Вы также можете просмотреть другие предлагаемые нами статьи, чтобы узнать больше –
Еженедельные упражнения по программированию знакомят с рядом передовых методов программирования на языке ассемблера z/OS.В частности, этот курс предоставит возможность разработать код ассемблера z/OS, который использует таблицы, списки и связанные списки, единицы работы z/OS, такие как TCB и многозадачность, методы и службы ассемблера z/OS для обеспечения высокого уровня доступности. , инструкции и методы сериализации на ассемблере z/OS, инструкции по z-архитектуре, многозадачность z/OS с использованием непривилегированных системных служб, программы с несколькими CSECT и код с повторным входом. Упражнения, используемые в классе, позволят учащемуся продолжить разработку передовых методов проектирования, написания кода и тестирования хорошо структурированного и хорошо документированного кода на ассемблере z/OS.
В частности, этот курс познакомит учащегося с ассемблерным кодом z/OS, который использует:
и даст им возможность разработать его.Таблицы, списки и связанные списки.
- единицы работы z/OS, такие как TCB и многозадачность.
- методы и службы ассемблера z/OS для обеспечения высокого уровня доступности (например, процедуры восстановления)
- Инструкции и методы ассемблера z/OS для сериализации (например, инструкции CS, , ENQ, защелки и блокировки)
- z инструкции по архитектуре, недавно добавленные в набор инструкций и представляющие интерес для разработчика приложений (например, BASR, BASM, инструкции относительно ветвления и т. д.)
- многозадачность z/OS с использованием непривилегированных системных служб (например, ATTACH/DETACH, WAIT/POST и т. д.)
- Программы с несколькими CSECT (например, использование связующего, передача параметров и данных с использованием областей данных и т. д.)
- Повторно вводимый код; учащийся будет продолжать использовать и развивать свой опыт в разработке и написании программ для повторного поступления
Упражнения, используемые в классе, позволят учащимся продолжить разработку передовых методов проектирования, кодирования и тестирования хорошо структурированного и хорошо документированного кода ассемблера z/OS. Кроме того, у студента будет возможность попрактиковаться в отладке и чтении существующего ассемблерного кода z/OS. Этот подход используется для имитации назначения задания, когда используется или модифицируется код ассемблера z/OS, но новая разработка может не использовать z/OS
Читайте также: