Как запустить программу на ассемблере в windows

Работа по теме: assmblr. Глава: 4. Выполнение (запуск) программы. ВУЗ: СПбГУТ.

Запуск
.EXE-программы
может быть осуществлён с помощью двойного
щелчка мышью.

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

2.4. Инструментальный пакет masm32

В
п.2.3 отмечено, что для создания программ
на ассемблере в Windows,
необходим текстовый редактор и компилятор.
Реальные программы Win32 используют также
внешние функции, стандартные константы
и переменные, ресурсы и много другое.
Всё это требует дополнительных файлов,
которые есть в инструментальном пакете
MASM32. Важно понять, что MASM32 не компилятор,
а сборник для программирования под
Win32, в который входит 32-битный компилятор
MASM.

Инструментальный
пакет MASM32
предназначен для создания приложений
Windows
на языке ассемблера и содержит все
необходимые инструменты, к тому же он
распространяется бесплатно.

Основные сведения и порядок работы в пакете masm32:

1.
Для создания исходных текстов программ
рекомендуется использовать текстовый
процессор пакета MASM32
под названием QEDITOR
(от Quick
Editor,
быстрый редактор):

После
набора сохранить текст программы
командой File
Save,
указать папку BIN
и расширение .ASM,
например MYPROG.ASM.

2.
Командой Project
Build
All
создать объектный и исполнимый файлы:

Если
исходный текст программы набран без
ошибок, то в папке, где он хранился,
увидим два новых файла: MYPROG.OBJ
и MYPROG.EXE.

3.
Когда объектный и исполнимый файлы
созданы, запустите программу на
выполнение. Для этого можно дважды
щелкнуть мышью по названию исполнимого
файла (в папке BIN)
или запустить программу через редактор
QEDITOR
командой Project
Run
Program.

2.5. Примеры

Пример
0
.
«Скелет»
стандартной программы

.386

.model flat, stdcall

option casemap :none

;подключение
необходимых
библиотек

include
MASM32INCLUDEwindows.inc

include
MASM32INCLUDEmasm32.inc

include
MASM32INCLUDEgdi32.inc

include
MASM32INCLUDEuser32.inc

include
MASM32INCLUDEkernel32.inc

includelib
MASM32LIBmasm32.lib

includelib
MASM32LIBgdi32.lib

includelib
MASM32LIBuser32.lib

includelib
MASM32LIBkernel32.lib

;раздел, где
объявляются все константы

.const

;раздел, где
объявляются переменные, уже имеющие
какое-то значение

.data

;раздел, где
объявляются переменные, еще не имеющие
значения

.data?

.code

start: ;с этого слова
начинается код программы

invoke ExitProcess,0

end start ;с этого
слова заканчивается код программы

Сохраните
этот «скелет» в отдельном файле для
удобства и используйте как заготовку.

Пример
1
.
Структура
программы и основные директивы

Построчно
разберём простейшую программу.

Текст
программы на ассемблере содержит кроме
инструкций процессору еще и служебную
информацию

(в виде директив),
предназначенную для программы-ассемблера.

Начнем
с простого. В первой программе не будет
вызовов API-функций,
ее цель – понять саму структуру программы
на языке ассемблера для Windows.
Поэтому программа, прибавляющая к 2
число 3, будет выглядеть следующим
образом:

.386

.model
flat, stdcall

.code

start:

mov
eax,
8

add
eax,
8

ret

end
start

В
ней инструкции процессора mov,
add,
ret
окружены
директивами. Первые три директивы
начинаются с точки.

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

Вторая
директива .model
flat,
stdcall
показывает,
в какой среде будет работать программа.
Все программы работают под управлением
операционной системы, которая их
запускает и обеспечивает взаимодействие
с внешней средой. Директива .model задаёт
модель памяти flat (плоская или сплошная)
для нашей программы. Эта модель памяти
используется для программирования под
Windows,
т.е. директива говорит о том, что именно
для операционных систем семейства
Windows
952
предназначена программа.

Stdcall
— это «уговор» о том, кто будет
чистить параметры (функция, которую
вызвали, или сам вызывающий). Мы всегда
будем использовать вариант «функция
чистит свои параметры». Он и называется
stdcall. Однако такое объяснение не полное,
и мы вернемся к параметру stdcall при
объяснении вызова функций. К этому
моменту вы уже будете знать, что такое
стек.

Третья
директива .code
показывает,
где начинаются сами команды процессора.
Когда операционная система пытается
запустить программу, она ищет в ней
инструкцию, с которой нужно начать, и
отправляет ее процессору. Когда же
инструкции кончаются, операционная
система «подхватывает» программу и
помогает ей правильно завершиться,
чтобы освободить место другим, ведь
Windows
– многозадачная операционная система,
способная выполнять одновременно
несколько программ. Уйти из-под «опеки»
операционной системы помогает инструкция
ret.

Инструкция,
с которой начинается программа, обычно
помечается последовательностью символов
с двоеточием на конце (меткой). В нашем
случае это start:.
Там, где оканчивается последовательность
команд процессора, в программе должна
стоять директива end
<метка первой инструкции программы>
,
в нашем случае это
end
start.
Эта директива, а также сама метка не
переводятся в инструкции ассемблера,
а лишь помогают получить программу,
которую способен выполнить процессор.
Без них программа-ассемблер не поймет,
с какой инструкции процессор начнет
работу.

Отладка

Программа
ничего не выводит на экран, поэтому за
для изучения её работы воспользуемся
программой-отладчиком OllyDbg.
Чтобы открыть программу в отладчике,
достаточно загрузить OllyDbg и открыть
программу как обычный документ –
File-Open.

В
верхней левой части отладчика можно
увидеть свою программу. Вверху справа
– регистры процессора.

Внизу
слева — байты памяти (OllyDbg сразу же
показывает секцию данных программы).
Внизу справа отображается содержимое
стека (работа со стеком будет описана
ниже).

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

С
помощью клавиши F8
можно выполнить программу по шагам и
просмотреть, как меняется содержимое
регистров. Ответьте на вопрос, почему
в результате сложения 8+8 регистр EAX
стал равен 10? Состояние флагов также
меняется. Мы видим, что после выполнения
первой команды флаг Z
опустился (обратился в ноль), потому что
результат выполнения операции не равен
нулю.

Пример
2
.
Использование
функций
API

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

API
– это стандартные функции, на основе
которых и пишут все программы для
Windows.
Например MessageBox выдаёт сообщение на
экран, PostQuitMessage сообщит Windows, что программа
хочет закончить работу и т.д. Все они
уже готовы – остается только вызывать
их. Получается, что используя API-функции,
мы применяем элементы программирования
высокого уровня.

Находятся
API-функции
обычно в динамически загружаемых
библиотеках – файлах с расширением
.DLL.

При
вызове каждой API-функции
надо передавать параметры, т.е. аргументы,
с которыми ей предстоит работать. Раньше
это делалось так: параметры заталкивались
в стек (команда push) задом наперед –
сначала последний, потом предпоследний
и т.д., а затем вызывалась сама программа
(команда call). Например:

push addr Text2

push addr Text1

push hWnd

call MessageBox

Такая
запись допускается и сейчас, но существует
и более компактная: invoke
MessageBox, hWnd, Text1, Text2

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

Рассмотрим
программу с использованием функций
API.
Прежде чем приступить к выводу на экран,
изучим более простую процедуру
ExitProcess.
Её
вызывает каждая Windows-программа,
чтобы завершить свою работу. В ассемблере
под DOS
мы пользовались инструкцией возврата
ret.
Но ExitProcess
действует
правильнее, не только возвращая управление
операционной системе, но и освобождая
занятые программой ресурсы.

В
следующем листинге показана программа
для Windows,
которая только и делает, что правильно
завершается.

.386

.model flat, stdcall

option casemap:none

includelib
C:MASM32LIBkernel32.lib

ExitProcess proto :DWORD

.code

start:

push 0

call
ExitProcess

end
start

Вызываемая
в ней процедура ExitProcess
требует
одного параметра – это код завершения,
возвращаемый операционной системе. Он
передается процедуре командой push
0
.
Число 0 считается признаком удачного
завершения.

Поскольку
ExitProcess

«чужая» процедура, не определенная в
нашей программе, ассемблер должен знать,
где она находится, а также (для проверки
– она ли это) число и размер ее параметров.

Сведения
об адресе и параметрах процедуры хранятся
в файле библиотеки kernel32.lib,
который подключается к ассемблерному
тексту директивой includelib
C:MASM32LIBkernel32.lib.

Перед
тем как создать инструкцию вызова этой
процедуры компоновщик сравнивает
сведения из библиотеки с прототипом
ExitProcess
proto
:
DWORD,
и если все совпадает, создает пригодный
к исполнению файл с расширением .EXE.
Прототип процедуры очень прост и состоит
из имени, слова proto
и
параметров. В нашем случае параметр
один – это двойное слово (то есть 4 байта)
DWORD.
Если параметров несколько, они разделяются
запятой.

Пример
3
.
Вывод
строки на экран

Создадим
программу, выводящую на экран фразу
“Hello,
world!”:

.386

.model
flat, stdcall

option casemap:none

ExitProcess proto :dword

GetStdHandle
proto :dword

WriteConsoleA proto :dword,
:dword, :dword, :dword, :dword

includelib
C:MASM32LIBkernel32.lib

.data

stdout dd ?

msg db “Hello, world!”,
0dh, 0ah

cWritten dd ?

.code

start:

invoke GetStdHandle, -11

mov stdout, eax

invoke WriteConsoleA,
stdout, ADDR msg, sizeof msg, ADDR cWritten, 0

invoke
ExitProcess,
0

end
start

В
программе вызываются три процедуры:
GetStdHandle,
WriteConsoleA
и
ExitProcess.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

В данном курсе планируется разобрать основные особенности программирования на самой простой реализации assembler – TASM. Этих знаний лично мне вполне хватило, чтобы на отлично сдать ЭВМ в институте и закончить все лабораторные работы. Во многих уроках будет домашние задание по их мотивам.

Для начала давайте установим наш старенький компилятор.
Ссылка

Почему именно vk.com?

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

После распаковки файлов, советую сохранить их в папке Asm на диск C, чтобы иметь меньше расхождений с представленным тут материалом.

Для запуска компилятора нам так же потребуется эмулятор DOSBox. Он и оживит все наши компоненты. Скачаем и установим его!
Ссылка

В папке Asm я специально оставил файл code.asm. Именно на нём мы и потренируемся запускать нашу программу. Советую сохранить его копию, ибо там хранится весь код, который в 99% случаев будет присутствовать в каждом вашем проекте.

code.asm

s_s segment
s_s ends

d_s segment
d_s ends

c_s segment
assume ss:s_s, ds:d_s, cs:c_s
begin:
mov ax, d_s
mov ds, ax
mov ax, 0

	; Your code needs to be here

mov ah, 4ch
int 21h
c_s ends
end begin

Итак. Запускаем наш DOSBox и видим следующее:

Для начала нам нужно смонтировать диск, на который вы сохранили вашу папку Asm. У меня это диск C, поэтому я прописываю следующую команду:

mount d: c:asm

Здесь d: это не реальный диск на вашем компьютере, а виртуальный. С таким же успехом вы можете назвать его i или s. А вот C это наоборот реальный диск. Мы прописываем путь до наших файлов ассемблера.

Теперь, откроем смонтированный диск:

d:

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

И только теперь мы начинает запускать наш файл! Бедные программисты 20 века, как они только терпели всё это? Пропишем следующую команду:

tasm code.asm

После мы увидим следующее сообщение, а наша директория пополнится новым файлом с расширением OBJ.

Теперь пропишем ещё одну команду:

tlink code.obj

В нашей папке появилась ещё пара файлов – CODE.MAP и  CODE.EXE. Последний как раз и есть исполняемый файл нашего кода assembler.

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

td code

Этот старинный интерфейс насквозь пропитан духом ушедшей эпохи старых операционных систем. Тем не менее…

Нажав F7 или fn + F7 вы сможете совершить 1 шаг по коду. Синяя строка начнёт движение вниз, изменяя значения регистров и флагов. Пока это всего лишь шаблон, на которым мы потренировались запускать нашу программу в режиме дебага. Реальное “волшебство” мы увидим лишь в следующих уроках. Пока давайте лишь ознакомимся с имеющимися разделами.

CS

Code segment – место, где turbo debug отражает все найденные строки кода. Важное замечание – все данные отражаются в TD в виде 16-ричной системы. А значит какая-нибудь ‘12’ это на самом деле 18, а реальное 12 это ‘C’. CS аналогичен разделу “Begin end.” на Pascal или функции main.

DS

Data segment, отражает пока все мусорные данные, которые обнаружил TD. Справа мы видим их символьную (char) интерпретацию. В будущем мы сможем увидеть здесь наш “Hello, world”, интерпретируемый компилятором в числа, по таблице ASCII. Хорошей аналогией DS является раздел VAR, как в Pascal. Пока будем считать, что это одно и тоже.

SS

Stack segment – место хранения данных нашего стека. Возможно, вы уже слышали о такой структуре данных. Если нет, обязуюсь пояснить в последующих статьях.

Регистры

Все эти ax, bx, cx, si, di, ss, cs и т. д. – это наши регистры, которые используются как переменные для хранения данных. Да, это очень грубое упрощение. Переменные из Pascal и регистры Assembler это не одно и тоже, но надеюсь, такая аналогия даёт более чёткую картину. Здесь мы сможем хранить данные о циклах, арифметических операциях, системных прерываниях и т. д.

Флаги

Все эти c, z, s, o, p и т.д. это и есть наши флаги. В них хранится промежуточная информация о том, например, было ли полученное число чётным, произошло ранее переполнение или нет. Они могут хранить результат побитого сдвига. По опыту, могу сказать, на них обращаешь внимание лишь при отладке программы, а не во время штатного исполнения.


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

Ещё одно замечание. Если вы измените данные исходного файла с расширением .ASM, то вам придётся совершить все ранее описанные операции вновь, ибо обновив например code.asm вы не меняете code.obj или code.exe.


Маленькая шпаргалка для заметок:

  1. mount d: c:asm – создаём виртуальный диск, где корень –папка asm

  2. d: — открываем созданный диск

  3. tasm code.asm – компилируем исходный код

  4. tlink code.obj – создаём исполняемый файл

  5. td code – запускаем debug

  6. F7 – делаем шаг в программе

Буду ждать комментарии от всех, кому интересен Assembler. Чувствую, я где-то мог накосячить в терминологии или обозначении того или иного элемента. Но статья на Habr отличный повод всё повторить.

Последнее обновление: 23.12.2022

Установка Arm GNU Toolchain на Windows

Наиболее популярным инструментом для компиляции кода ассемблера для arm представляет компилятор GAS от проекта GNU, который входит в состав комплекта инструментов
для разработки под ARM — Arm GNU Toolchain. Итак, вначале установим данный набор инструментов. Для этого перейдем на официальный сайт компании Arm
на страницу https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads.
Здесь представлены поседние версии Arm GNU Toolchain для разных архитектур.

GNU Arm Embedded Toolchain для ARM64 ассемблера

Каждая версия Arm GNU Toolchain привязана к определенной версии компиляторов GCC. Например, версия Arm GNU Toolchain 12.2.Rel1
привязан к версии 12.2 набора компиляторов gcc.

Для ОС Windows доступно несколько групп пакетов по различные архитектуры:

  • AArch32 bare-metal target (arm-none-eabi): для компиляции программ под 32-битные архитектуры без привязки к конкретной операционной системе

  • AArch32 GNU/Linux target with hard float (arm-none-linux-gnueabihf): для компиляции программ под 32-битную ОС Linux

  • AArch64 bare-metal target (aarch64-none-elf): для компиляции программ под 64-битные архитектуры без привязки к конкретной операционной системе

  • AArch64 GNU/Linux target (aarch64-none-linux-gnu): для компиляции программ под 64-битную ОС Linux

Как видно из названия, наборы компиляторов имеют названия типа arm-none-linux-gnueabi, arm-none-eabi, arm-eabi и т.д. Все эти названия формируются по шаблону

arch[-vendor] [-os] - eabi
  • arch: указывает на архитектуру

  • vendor: указывает на производителя

  • os: указывает на целевую операционную систему

  • eabi: сокращение от Embedded Application Binary Interface

Например, пакет инструментов arm-none-eabi предназначен для 32-х битной архитектуры, не зависит от конкретного вендора, операционной системы и компилируется с помощью ARM EABI.

Другой пример: ракет инструментов arm-none-linux-gnueabi предназначен для 32-х битной архитектуры, но создает бинарники непосредственно для ОС Linux и использует GNU EABI.

Поскольку в данном случае в данном случае мы рассматриваем именно arm64, то нас будет интересовать прежде всего те пакеты, которые начинаются на AArch64.
И поскольку пакет AArch64 bare-metal target (aarch64-none-elf) не привязан к определенной ОС, то выберем его. Кроме того, он доступен для всех основных ОС. Однако отмечу, что, если планируется писать код именно под
Linux (в том числе Android), то лучше использовать AArch64 GNU/Linux target (aarch64-none-linux-gnu) — он создает более компактные (иногда намного меньшие) исполняемые файлы.

Для Windows доступны пакеты в двух вариантах: установочный файл exe, который устанавливает все необходимые файлы в папку C:Program Files (x86), и
zip-архив — по сути те же самые файлы, которые мы можем распаковать в любое нужное для нас расположение. Большой разницы между файлами из exe и zip нет, но для простоты выберем exe-файл (в моем случае это файл
arm-gnu-toolchain-12.2.rel1-mingw-w64-i686-aarch64-none-elf.exe

Установка GNU Arm Embedded Toolchain для ARM64 ассемблера под Windows

После загрузки запустим установщик

Установка компилятора gas из GNU Arm Embedded Toolchain для ARM64 под Windows

Прощелкаем по шагам и в конце на последнем окне после установки установим флажок Add path to environment variable, чтобы добавить путь к компилятору и другим инструментам
в переменные среды:

Добавление пути к компилятору GNU Arm Embedded Toolchain для ARM64 в переменные среды

Если мы посмотрим на добавленный в переменные среды путь (в данном случае каталог C:Program Files (x86)Arm GNU Toolchain aarch64-none-elf12.2 rel1bin),
то мы найдем файлы компилятора и ряд других файлов:

ассемблер as из GNU Arm Embedded Toolchain для ARM64 под Windows

В этом комплекте нам понадобится прежде всего сам ассемблер — файл aarch64-none-elf-as.exe, который по коду ассемблера Arm64 создает объектный файл. Кроме того,
также потребуется файл aarch64-none-elf-as.ld, который также располагается в этой папке и который генерирует из объектного файла исполныемый файл.

Для проверки настройки откроем терминал/командную строку и выведем версию компилятора следующей командой:

aarch64-none-elf-as --version

Мы должны получить вывод типа следующего:

GNU assembler (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 2.39.0.20221210
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `aarch64-none-elf'.

C:Userseugen>

Создание первой программы

Теперь напишем первую простейшую программу, которая просто будет выводить на консоль некоторую строку. Для этого создадим на жестком диске какой-нибудь каталог, например, C:arm.
Создадим в этого каталоге новый файл hello.s (обычно файлы с кодом ассемблера arm имеют расширение .s). Определим в этом файл следующий код:

.global _start          // устанавливаем стартовый адрес программы

_start: mov X0, #1          // 1 = StdOut - поток вывода
 ldr X1, =hello             // строка для вывода на экран
 mov X2, #19                // длина строки
 mov X8, #64                // устанавливаем функцию Linux
 svc 0                      // вызываем функцию Linux для вывода строки

 mov X0, #0                 // Устанавливаем 0 как код возврата
 mov X8, #93                // код 93 представляет завершение программы
 svc 0                      // вызываем функцию Linux для выхода из программы

.data
hello: .ascii "Hello METANIT.COM!n"    // данные для вывода

Для большего понимания я снабдил программу комментариями. GNU Assembler использует тот же самый синтаксис комментариев, что и C/C++ и другие си-подобные
языки: одиночный комментарий начинается с двойного слеша //. Также можно использовать многострочный комментарий с помощью символов /∗ и ∗/, между которыми помещается
текст комментария (/* текст комментария */

Вначале надо указать линкеру (в нашем случае программа ld) стартовую точку программы. В данной программе стартовая точка программы проецируется
на метку _start. И чтобы линкер получил к ней доступ, определяет _start в качестве глобальной переменной с помощью оператора global.

.global _start

Одна программа может состоять из множества файлов, но только один из них может иметь точку входа в программу _start

Далее идут собственно действия программы. Вначале вызывается инструкция mov, которая помещает данные в регистр.

mov X0, #1

Значения X0-X2 представляют регистры для параметров функции в Linux. В данном случае помещаем в регистр X0 значение «#1». Операнды начинаются со знака «#»
Число 1 представляет номер стандартного потока вывода «StdOut», в данном случае, грубо говоря, вывод на консоль.

Далее загружаем в регистр X1 адрес строки для вывода на экран с помощью инструкции ldr

ldr X1, =hello 

Затем также с помощью инструкции mov помещаем в регистр X2 длину выводимой строки

mov X2, #19

Для любого системного вызова в Linux параметры помещаются в регистры X0–X7 в зависимости от количества. Затем в регистр X0 помещается код возврата. А сам системный вызов
определяется номером функции из регистра X8. Здесь помещаем в X8 функцию с номеро 64 (функция write)

mov X8, #64

Далее выполняем системный вызов с помощью оператора svc

svc 0 

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

После этого нам надо выйти из программы. Для этого помещаем в регистр X0 число 0

mov X0, #0

А в регистр X8 передаем число 93 — номер функции для выхода из программы (функция exit)

mov X8, #93

И с помощью svc также выполняем функции. После этого программа должна завершить выполнение.

В самом конце программы размещена секция данных

.data
hello: .ascii "Hello METANIT.COM!n"    // данные для вывода

Оператор .data указывает, что дальше идет секция данных. Выражение .ascii выделяет память и помещает в нее указанную далее строку.

Строка завершается символом перевода строки «n», чтобы не надо было нажимать на Return, чтобы увидеть текст в окне терминала.

Компиляция приложения

Для компиляции приложения откроем терминал/командную строку и командой cd перейдем к папке, где расположен файл hello.s с
исходным кодом программы. И для компиляции выполним команду:

aarch64-none-elf-as hello.s -o hello.o

Компилятору aarch64-none-elf-as в качестве параметра передается файл с исходным кодом hello.s. А параметр -o указывает, в какой файл будет компилироваться
программа — в данном случае в файл hello.o. Соответственно в папке программы появится файл hello.o

Затем нам нужно скомпновать программу с исполняемый файл с помощью линкера aarch64-none-elf-ld командой:

aarch64-none-elf-ld hello.o -o hello

Полный вывод:

C:Userseugen>cd c:arm

c:arm>aarch64-none-elf-as hello.s -o hello.o

c:arm>aarch64-none-elf-ld hello.o -o hello

c:arm>

После этого в папке программы появится исполняемый файл hello, который мы можем запускать на устройстве с архитектурой ARM под управлением Linux.

компиляция и линковка программы на ассемблере arm64 на Windows

Тестирование приложения на Android

Итак, у нас есть исполняемый файл программы. Мы ее можем протестировать. Для этого нам нужен Linux на устройстве с архитектурой ARM. В качестве такого устройства я возьму самый распространенный вариант
— смартфон под управлением Android. Поскольку Android построен на базе Linux и как правило устанавливается на устройства с arm архитектурой.

Для установки файла на Android нам понадобится консольная утилита adb, которая устанавливается в рамках Android SDK. Android SDK обычно устанавливается
вместе с Android Studio. Но если Android Studio не установлена, то можно загрузить пакет https://dl.google.com/android/repository/platform-tools-latest-windows.zip.
В составе этого пакета или в составе SDK в папке platforms-tools можно найти нужную нам утилиту adb.

тестирование на Android программы на ассемблере arm64, скомпилированной на Windows

Для упрощения работы путь к этой утилите лучше добавить в переменные среды, чтобы не прописывать к ней полный путь.

Теперь переместим скомпилированный файл hello на устройство под Android. Для этого подключим через usb к компьютеру смарфтон с Android и перейдем в консоли с помощью команды cd к папке с файлом hello используем следующую команду

[Путь_к_файлу_adb]/adb push hello /data/local/tmp/hello

То есть в данном случае используем команду push для помещения копии файла hello на смартфон в папку /data/local/tmp/

Установка программы на ассемблере arm64 на Android

Далее перейдем к консоли устройства Android с помощью команды:

Далее перейдем к папке /data/local/tmp с помощью команды

Затем изменим режим файла, чтобы его можно было запустить:

и в конце выполним файл hello

И на консоль должна быть выведена строка «Hello METANIT.COM!»

Первая программа на ассемблере arm64 на Android с компиляцией на Windows

Assembler


Рекомендация: подборка платных и бесплатных курсов Smm — https://katalog-kursov.ru/

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

MASM

Используется для создания драйверов под Windows.

По ссылке переходим на сайт и скачиваем пакет (masm32v11r.zip). После инсталляции программы на диске создается папка с нашим пакетом C:masm32. Создадим программу prog11.asm, которая ничего не делает.

.586P
.model flat, stdcall 
_data segment 
_data ends
_text segment
start:
ret   
_text ends
end start

Произведём ассемблирование (трансляцию) файла prog11.asm, используя ассемблер с сайта.

image
Ключ /coff используется здесь для трансляции 32-битных программ. Как сказано в Википедии, «MASM — один из немногих инструментов разработки Microsoft, для которых не было отдельных 16- и 32-битных версий.»

Также ассемблер версии 6. можно взять на сайте Кипа Ирвина kipirvine.com/asm, автора книги «Язык ассемблера для процессоров Intel».

Кстати, вот ссылка на личный сайт Владислава Пирогова, автора книги “Ассемблер для Windows”.

MASM с сайта Microsoft

Далее скачаем MASM (версия 8.0) с сайта Microsoft по ссылке. Загруженный файл носит название «MASMsetup.exe». При запуске этого файла получаем сообщение -«Microsoft Visual C++ Express Edition 2005 required».

Открываем этот файл архиватором (например 7zip). Внутри видим файл setup.exe, извлекаем его, открываем архиватором. Внутри видим два файла vc_masm.msi,vc_masm1.cab. Извлекаем файл vc_masm1.cab, открываем архиватором. Внутри видим файл FL_ml_exe_____X86.3643236F_FC70_11D3_A536_0090278A1BB8. Переименовываем его в файл fl_ml.exe, далее, произведём ассемблирование файла prog11.asm, используя ассемблер fl_ml.exe.

image

MASM в Visual Studio

Также MASM можно найти в папке с Visual Studio (у меня VS 10) вот здесь: C:Program FilesMicrosoft Visual Studio 10.0VCbinml.exe.

image

Для того, чтобы запустить на 32- или 64-разрядной системе и создавать программы, работающие как под 32-, так и под 64-разрядной Windows, подходит MASM32 (ml.exe, fl_ml.exe). Для того, чтобы работать на 32- и 64-разрядных системах и создавать программы, работающие под 64-разрядной Windows, но неработающие под 32-разрядной нужен ассемблер ml64.exe. Лежит в папке C:Program FilesMicrosoft Visual Studio 10.0VCbinamd64 и вот здесь — C:Program FilesMicrosoft Visual Studio 10.0VCbinx86_amd64.

TASM

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

Скачать можно, например, здесь. Инсталлятора нет, просто извлекаем программу. Вот исходник из книги Питера Абеля (рис. 3.2) «Язык Ассемблера для IBM PC и программирования».

stacksg segment para stack 'stack'
    db 12 dup ('stackseg')
stacksg ends
codesg segment para 'code'
begin proc far
 assume ss:stacksg,cs:codesg,ds:nothing
 push ds
 sub ax,ax
 push ax
 mov ax, 0123h
 add ax, 0025h
 mov bx,ax
 add bx,ax
 mov cx,bx
 sub cx,ax
 sub ax,ax
 nop
 ret
begin endp
codesg ends
 end begin

Выполним ассемблирование (трансляцию) файла abel32.asm.

image

Корректность работы программы можно проверить, произведя линковку (компоновку) объектного файла и запустив полученный файл в отладчике.

Как было сказано выше, MASM можно использовать для работы с 16-битными программами. Выполним ассемблирование (трансляцию) программы abel32.asm с помощью ассемблера MASM:

image

Ключ /coff здесь не используем.

FASM

В статье Криса Касперски [RIP Мыщъх] «Сравнение ассемблерных трансляторов» написано, что «FASM — неординарный и весьма самобытный, но увы, игрушечный ассемблер. Пригоден для мелких задач типа „hello, world“, вирусов, демок и прочих произведений хакерского творчества.»

Скачаем FASM с официального сайта. Инсталлятора нет, просто извлекаем программу. Откроем fasm editor — C:fasmfasmw.exe. В папке C:fasmEXAMPLESHELLO есть файл HELLO.asm.

include 'win32ax.inc' 
.code
  start:
 invoke    MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
    invoke    ExitProcess,0
.end start

Откроем файл HELLO.asm из fasmw.exe. Изменим строку include ‘win32ax.inc’ на строку include ‘c:fasmINCLUDEWIN32AX.INC’. Запускаем из меню Run > Run.

image

Вот ссылки на ресурсы, посвященные FASM:

> FASM на Cyberforum’е
> FASM на asmworld
> Цикл статей «Ассемблер под Windows для чайников»
> Сайт на narod’е

FASM в Linux

Для того, использовать FASM в Linux (у меня Ubuntu), скачаем соответствующий дистрибутив (fasm-1.71.60.tgz), распакуем его, в папке у нас будет бинарный файл fasm, копируем этот файл в /usr/local/bin для того, чтобы можно было запускать его из консоли, как любую другую команду.Выполним ассемблирование программы hello.asm из папки fasm/examples/elfexe/hello.asm.

image

Корректность работы программы можно проверить в отладчике.

Nasm

Nasm успешно конкурирует со стандартным в Linux- и многих других UNIX-системах ассемблером Gas.

Nasm в Linux можно установить его с помощью менеджера пакетов программного обеспечения с графическим интерфейсом, команды yum install nasm, apt-get install nasm или любой другой команды, актуальной для вашего дистрибутива. Создадим программу, которая 5 раз выводит сообщение “Hello”. Пример взят из книги Андрея Викторовича Столярова “Программирование на языке ассемблера NASM для ОС UNIX”. Учебник, а также библиотека “stud_io.inc” есть на личном сайте автора.

%include "stud_io.inc"
global _start
section .text
_start: mov eax, 0
again:  PRINT "Hello"
PUTCHAR 10
inc eax
cmp eax, 5
jl again
FINISH

Выполним ассемблирование и линковку и запустим файл hello.asm.

$ nasm -f elf hello.asm
$ ld hello.o -o hello
$ ./hello

NASM для Windows

NASM для Windows можно установить, скачав соответствующий дистрибутив с соответствующего сайта.

Ссылки на ресурсы, посвященные Nasm:

> Сайт А.В. Столярова
> Сайт, на котором лежит электронный учебник (в архиве)
> То же самое

AS

Стандартный ассемблер практически во всех разновидностях UNIX, в том числе Linux и BSD. Свободная версия этого ассемблера называется GAS (GNU assembler). Позволяет транслировать программы с помощью компилятора GCC.

Из учебников удалось найти только книгу на английском «Programming from the ground up». На русском удалось найти только одну главу из книги С. Зубкова «Assembler для DOS, Windows и UNIX».

Возьмем пример программы, которая ничего не делает, с сайта. Создадим программу gas.s

.section .text
   .globl _start
   _start:
      movl  $1, %eax
      movl  $2, %ebx
      int   $0x80

Выполним ассемблирование (трансляцию), линковку и запуск программы:

$ as -o gas.o gas.s
$ ld -o gas gas.o
$ ./gas

Если в данной программе изменить _start на main, то можно выполнить ассемблирование (трансляцию) и линковку компилятором gcc.

.section .text
   .globl main
   main:
      movl  $1, %eax
      movl  $2, %ebx
      int   $0x80

Выполним ассемблирование (трансляцию), линковку и запуск программы:

$ gcc gas.s -o gas
$ ./gas

Выводы: если вы изучаете программирование под Windows, то вы можете остановить свой выбор на Masm; Tasm больше не поддерживается, но для обучения по старым классическим учебникам подойдёт.

Под Linux Gas подойдет тем, кто использует GCC, а тем, кому не нравится синтаксис Gas, подойдёт Nasm.

Ты решил освоить ассемблер, но не знаешь, с чего начать и какие инструменты для этого нужны? Сейчас расскажу и покажу — на примере программы «Hello, world!». А попутно объясню, что процессор твоего компьютера делает после того, как ты запускаешь программу.

Содержание

  1. Основы ассемблера
  2. Если наборы инструкций у процессоров разные, то на каком учить ассемблер лучше всего?
  3. Что и как процессор делает после запуска программы
  4. Регистры процессора: зачем они нужны, как ими пользоваться
  5. Подготовка рабочего места
  6. Написание, компиляция и запуск программы «Hello, world!»
  7. Инструкции, директивы
  8. Метки, условные и безусловные переходы
  9. Комментарии, алгоритм, выбор регистров
  10. Взаимодействие с пользователем: получение данных с клавиатуры
  11. Полезные мелочи: просмотр машинного кода, автоматизация компиляции
  12. Выводы

Основы ассемблера

Я буду исходить из того, что ты уже знаком с программированием — знаешь какой-нибудь из языков высокого уровня (С, PHP, Java, JavaScript и тому подобные), тебе доводилось в них работать с шестнадцатеричными числами, плюс ты умеешь пользоваться командной строкой под Windows, Linux или macOS.

Если наборы инструкций у процессоров разные, то на каком учить ассемблер лучше всего?

Знаешь, что такое 8088? Это дедушка всех компьютерных процессоров! Причем живой дедушка. Я бы даже сказал — бессмертный и бессменный. Если с твоего процессора, будь то Ryzen, Core i9 или еще какой-то, отколупать все примочки, налепленные туда под влиянием технологического прогресса, то останется старый добрый 8088.

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

РЕКОМЕНДУЕМ:
Лучшие игры для программистов и технарей

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

Что и как процессор делает после запуска программы

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

Некоторые инструкции занимают один байт памяти, другие два, три или больше. Они выглядят как-то так:

90

B0 77

B8 AA 77

C7 06 66 55 AA 77

Вернее, даже так:

90 B0 77 B8 AA 77 C7 06 66 55 AA 77

Хотя погоди! Только машина может понять такое. Поэтому много лет назад программисты придумали более гуманный способ общения с компьютером: создали ассемблер.

Благодаря ассемблеру ты теперь вместо того, чтобы танцевать с бубном вокруг шестнадцатеричных чисел, можешь те же самые инструкции писать в мнемонике:

nop

mov al, 0x77

mov ax, 0x77AA

mov word [0x5566], 0x77AA

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

Регистры процессора: зачем они нужны, как ими пользоваться

Что делает инструкция
mov? Присваивает число, которое указано справа, переменной, которая указана слева.

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

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

У процессора 8088 регистры 16-битные, их восемь штук (в скобках указаны типичные способы применения регистра):

  • AX — общего назначения (аккумулятор);
  • BX — общего назначения (адрес);
  • CX — общего назначения (счетчик);
  • DX — общего назначения (расширяет
    AX до 32 бит);
  • SI — общего назначения (адрес источника);
  • DI — общего назначения (адрес приемника);
  • BP — указатель базы (обычно адресует переменные, хранимые на стеке);
  • SP — указатель стека.

Несмотря на то что у каждого регистра есть типичный способ применения, ты можешь использовать их как заблагорассудится. Четыре первых регистра —
AX,
BX,
CX и
DX — при желании можно использовать не полностью, а половинками по 8 бит (старшая
H и младшая
L):
AH,
BH,
CH,
DH и
AL,
BL,
CL,
DL. Например, если запишешь в
AX число
0x77AA (
mov ax, 0x77AA), то в
AH попадет
0x77, в
AL
0xAA.

С теорией пока закончили. Давай теперь подготовим рабочее место и напишем программу «Hello, world!», чтобы понять, как эта теория работает вживую.

Подготовка рабочего места

  1. Скачай компилятор NASM с www.nasm.us. Обрати внимание, он работает на всех современных ОС: Windows 10, Linux, macOS. Распакуй NASM в какую-нибудь папку. Чем ближе папка к корню, тем удобней. У меня это
    c:nasm (я работаю в Windows). Если у тебя Linux или macOS, можешь создать папку
    nasm в своей домашней директории.
  2. Тебе надо как-то редактировать исходный код. Ты можешь пользоваться любым текстовым редактором, который тебе по душе: Emacs, Vim, Notepad, Notepad++ — сойдет любой. Лично мне нравится редактор, встроенный в Far Manager, с плагином Colorer.
  3. Чтобы в современных ОС запускать программы, написанные для 8088, и проверять, как они работают, тебе понадобится DOSBox или VirtualBox.

Написание, компиляция и запуск программы «Hello, world!»

Сейчас ты напишешь свою первую программу на ассемблере. Назови ее как хочешь (например,
first.asm) и скопируй в папку, где установлен
nasm.

Основы ассемблера. Hello World

Если тебе непонятно, что тут написано, — не переживай. Пока просто постарайся привыкнуть к ассемблерному коду, пощупать его пальцами. Чуть ниже я все объясню. Плюс студенческая мудрость гласит: «Тебе что-то непонятно? Перечитай и перепиши несколько раз. Сначала непонятное станет привычным, а затем привычное — понятным».

Теперь запусти командную строку, в Windows это cmd.exe. Потом зайди в папку
nasm и скомпилируй программу, используя вот такую команду:

nasm f bin first.asm o first.com

Если ты все сделал правильно, программа должна скомпилироваться без ошибок и в командной строке не появится никаких сообщений.
NASM просто создаст файл
first.com и завершится.

Чтобы запустить этот файл в современной ОС, открой DOSBox и введи туда вот такие три команды:

Само собой, вместо
c:nasm тебе надо написать ту папку, куда ты скопировал компилятор. Если ты все сделал правильно, в консоли появится сообщение «Hello, world!».

Основы ассемблера. Hello World

Инструкции, директивы

В нашей с тобой программе есть только три вещи: инструкции, директивы и метки.

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

Директивы (в нашей программе их две:
org и
db) — это распоряжения, которые ты даешь компилятору. Каждая отдельно взятая директива говорит компилятору, что на этапе ассемблирования нужно сделать такое-то действие. В машинный код директива не переводится, но она влияет на то, каким образом будет сгенерирован машинный код.

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

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

В нашем случае:
db «Hello, world», ‘!’, 0.

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

Метки, условные и безусловные переходы

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

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

РЕКОМЕНДУЕМ:
Язык программирования Ада

У тебя в распоряжении есть одна инструкция безусловного перехода (
jmp) и штук двадцать инструкций условного перехода.

В нашей программе задействованы две инструкции перехода:
je и
jmp. Первая выполняет условный переход (Jump if Equal — прыгнуть, если равно), вторая (Jump) — безусловный. С их помощью мы организовали цикл.

Обрати внимание: метки начинаются либо с буквы, либо со знака подчеркивания, либо со знака собаки. Цифры вставлять тоже можно, но только не в начало. В конце метки обязательно ставится двоеточие.

Комментарии, алгоритм, выбор регистров

Итак, в нашей программе есть только три вещи: инструкции, директивы и метки. Но там могла бы быть и еще одна важная вещь: комментарии. С ними читать исходный код намного проще.

Как добавлять комментарии? Просто поставь точку с запятой, и все, что напишешь после нее (до конца строки), будет комментарием. Давай добавим комментарии в нашу программу.

Основы ассемблера. Комментарии, алгоритм, выбор регистров

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

  1. Поместить в
    BX адрес строки.
  2. Поместить в
    AL очередную букву из строки.
  3. Если вместо буквы там 0, выходим из программы — переходим на 6-й шаг.
  4. Выводим букву на экран.
  5. Повторяем со второго шага.
  6. Конец.

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

Взаимодействие с пользователем: получение данных с клавиатуры

От программ, которые не могут взаимодействовать с пользователем, толку мало. Так что смотри, как можно считывать данные с клавиатуры. Сохрани вот этот код как
second.asm.

Основы ассемблера. Получение данных с клавиатуры

Потом иди в командную строку и скомпилируй его в NASM:

nasm f bin second.asm o second.com

Затем запусти скомпилированную программу в DOSBox:

Как работает программа? Две строки после метки
@@start вызывают функцию BIOS, которая считывает символы с клавиатуры. Она ждет, когда пользователь нажмет какую-нибудь клавишу, и затем кладет ASCII-код полученного значения в регистр
AL. Например, если нажмешь заглавную
A, в
AL попадет
0x41, а если строчную
A
0x61.

Дальше смотрим: если нажата клавиша с кодом 0x1B (клавиша ESC), то выходим из программы. Если же нажата не ESC, вызываем ту же функцию, что и в предыдущей программе, чтобы показать символ на экране. После того как покажем — прыгаем в начало (
jmp):
start.

Обрати внимание, инструкция
cmp (от слова compare — сравнить) выполняет сравнение, инструкция
je (Jump if Equal) — прыжок в конец программы.

Полезные мелочи: просмотр машинного кода, автоматизация компиляции

Если тебе интересно, в какой машинный код преобразуются инструкции программы, скомпилируй исходник вот таким вот образом (добавь опцию
l):

nasm f bin second.asm l second.lst o second.com

Тогда NASM создаст не только исполняемый файл, но еще и листинг:
second.lst. Листинг будет выглядеть как-то так.

Основы ассемблера. Просмотр машинного кода, автоматизация компиляции

Еще тебе наверняка уже надоело при каждом компилировании вколачивать в командную строку длинную последовательность одних и тех же букв. Если ты используешь Windows, можешь создать батник (например,
m.bat) и вставить в него вот такой текст.

Основы ассемблера. Автоматизация компиляции

Теперь ты можешь компилировать свою программу вот так:

Само собой, вместо
first ты можешь подставить любое имя файла.

Выводы

Итак, ты теперь знаешь, как написать простейшую программу на ассемблере, как ее скомпилировать, какие инструменты для этого нужны. Конечно, прочитав одну статью, ты не станешь опытным программистом на ассемблере. Чтобы придумать и написать на нем что-то стоящее — вроде Floppy Bird и «МикроБ», которые написал я, — тебе предстоит еще много пройти. Но первый шаг в эту сторону ты уже сделал.

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (2 оценок, среднее: 5,00 из 5)

Загрузка…

Понравилась статья? Поделить с друзьями:
  • Как запустить сканирование с компьютера для windows 10
  • Как запустить программу на windows xp предназначенную для windows 7
  • Как запустить службу windows update windows 7
  • Как запустить службу windows installer в безопасном режиме в win 10
  • Как запустить программу на python через консоль windows