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):

user$ nasm -f macho hello.asm

(для Линукса указываем формат elf, а не macho)

Запускаем компоновщик для получения исполняемого файла (указываем точку входа, конечное имя исполняемого файла программы и объектный файл):

user$ ld -e _start -o hello hello.o

Если всё нормально, то запускаем программу и наслаждаемся результатом:

user$ ./hello Hello, world! user$

Вариант "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

host$ vim exit.s
# Это комментарий для GAS
.section .data

.section .text
.globl _start

_start:
    movl $1, %eax   # 1 - exit (системный вызов)
    movl $0, %ebx
    int $0x80

Собираем и линкуем

host$ as exit.s -o exit.o host$ ld exit.o -o exit

Проверяем

host$ ./exit host$ echo $? 0

.section .data

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

.section .text

Секция текста программы, это то место, в котором живут инструкции программы.

.globl _start

Такую инструкцию ассемблера как _start, необходимо запомнить. _start - это символ (symbol), что значит, что он будет заменён чем-либо другим в процессе сборки или компоновки (линковки). Символы обычно используются как метка для области кода или данных, поэтому вы можете ссылаться ссылаться на них по их имени, вместо номера их размещения. Представьте, что вам необходимо ссылаться на на каждое место в памяти по его адресу. Во-первых, это будет очень неудобно, потому что вам придётся запоминать или искать числовые адреса в памяти для каждого куска кода или данных. В дополнение, каждый раз, когда вам нужно будет вставить кусок данных или кода, вам придётся менять все адреса в вашей программе. Символы используются для того чтобы ассемблер и компоновщик могли отслеживать адреса, а вы бы могли сконцентрироваться на написании вашей программы.

.globl означает, что ассемблер не должен удалять этот символ после сборки, потому что он нужен компоновщику. _start - это специальный символ, который всегда должен быть обозначен как .globl (глобальный), потому что он является меткой начала начала программы. Без такого обозначения этого места, когда компьютер загрузит вашу программу, он не будет знать с какого места начать её.

_start:

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

Поддержите проект, если он помог вам

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