HELLO WORLD!
Итак. Это моя первая программа, написанная на Ассемблере. Точнее сказать скопированная с одного из замечательных сайтов, но я ввёл её сам, сам откомпилировал и скомпоновал. И, о чудо, она заработала. Счастью не было предела. Правда хоть сайт и про Линукс, но делал я её на Маке, т.к. увы и ах, но Debian пока не удалось сюда поставить. Пока что...)
Итак, поехали. Написание простейшей программы выводящей "Hello world!" при помощи системного вызова.
ОС - Mac OS X 10.9.4 синтаксис языка - Intel компилятор - NASM 0.98.40 (http://www.nasm.us/) компоновщик - ld64-236.4 Исходный код программы (взято отсюда): Создаём файл: user$ touch hello.asm Открываем его в своём редакторе: user$ vim hello.asm Вводим туда саму программу:
section .text
global _start ;должна быть объявлена для компоновщика (ld)
_syscall:
int 0x80 ;системный вызов
ret
_start: ;точка входа
push dword len ;длина строки
push dword msg ;строка для вывода
push dword 1 ;дескриптор файла (stdout)
mov eax,0x4 ;номер системного вызова (sys_write)
call _syscall ;вызываем ядро
;альтернативный вариант вызова ядра:
;push eax
;call 7:0
add esp,12 ;очищаем стек (3 arguments * 4)
push dword 0 ;код выхода из программы
mov eax,0x1 ;номер системного вызова (sys_exit)
call _syscall ;вызываем ядро
;мы не вернёмся из sys_exit,
;поэтому нет необходимости очищать стек
section .data
msg db "Hello, world!",0xa ;наша строка 0xa - \n (перенос строки)
len equ $ - msg ;длина строки
Компилируем исходный код и получаем объектный файл (указываем формат бинарного файла и получаем объектный файл hello.o):
(для Линукса указываем формат elf, а не macho)
Запускаем компоновщик для получения исполняемого файла (указываем точку входа, конечное имя исполняемого файла программы и объектный файл):
Если всё нормально, то запускаем программу и наслаждаемся результатом:
Вариант "Hello, world!" для GAS
.section .data
msg:
.ascii "Hello, world!\n"
.set len, . - msg
.section .text
.globl _start
_start:
# write
mov $4, %eax
mov $1, %ebx
mov $msg, %ecx
mov $len, %edx
int $0x80
# exit
mov $1, %eax
xor %ebx, %ebx
int $0x80
Программа Exit (для GAS)
ОС - Ubuntu 18.04.3 LTSсинтаксис языка - AT&T
компилятор - GNU ассемблер, версия 2.30 (x86_64-linux-gnu)
компоновщик - GNU ld 2.30
# Это комментарий для GAS
.section .data
.section .text
.globl _start
_start:
movl $1, %eax # 1 - exit (системный вызов)
movl $0, %ebx
int $0x80
Собираем и линкуем
Проверяем
.section .data
Всё, что начинается с точкине транслируется напрямую в инструкции процессора. Это инструкции самого ассемблера. Они называются директивами ассемблера или псевдооперациями, т.к. они обрабатываются ассемблером, а не выполяются компьютером. Команда .section разбивает вашу программу на секции. Этой командой начинается секция данных, где вы указываете любое хранилище памяти, которое вам нужно для данных. В программе Exit данные не используются, поэтому эта секция нам не нужна. Она здесь просто для упоминания. Почти все последующие программы будут содержать данные.
.section .text
Секция текста программы, это то место, в котором живут инструкции программы.
.globl _start
Такую инструкцию ассемблера как _start, необходимо запомнить. _start - это символ (symbol), что значит, что он будет заменён чем-либо другим в процессе сборки или компоновки (линковки). Символы обычно используются как метка для области кода или данных, поэтому вы можете ссылаться ссылаться на них по их имени, вместо номера их размещения. Представьте, что вам необходимо ссылаться на на каждое место в памяти по его адресу. Во-первых, это будет очень неудобно, потому что вам придётся запоминать или искать числовые адреса в памяти для каждого куска кода или данных. В дополнение, каждый раз, когда вам нужно будет вставить кусок данных или кода, вам придётся менять все адреса в вашей программе. Символы используются для того чтобы ассемблер и компоновщик могли отслеживать адреса, а вы бы могли сконцентрироваться на написании вашей программы.
.globl означает, что ассемблер не должен удалять этот символ после сборки, потому что он нужен компоновщику. _start - это специальный символ, который всегда должен быть обозначен как .globl (глобальный), потому что он является меткой начала начала программы. Без такого обозначения этого места, когда компьютер загрузит вашу программу, он не будет знать с какого места начать её.
_start:
Определяет значение метки _start. Метка - это символ, завершаемый двоеточием. Метки определяют значения символов. Когда ассемблер собирает программу, он должен присвоить каждому значению данных и инструкции адрес. Метки указывают ассемблеру, что значение символа будет равно тому, где находится следующая инструкция или элемент данных. В этом случае, если реальное физическое размещение данных или инструкций изменится, то вам не придётся переписывать любые ссылки на него - символ автоматически получит новое значение.