Ассемблер
Как писать на ассемблере на Linux
Писать на ассемблере на Linux лучше начинать с понятной связки: архитектура x86-64, NASM-синтаксис, объектный формат elf64, сборка через nasm, linking через ld или gcc, запуск из терминала. Linux хорошо подходит для изучения ассемблера, потому что инструменты сборки доступны из командной строки, а системные вызовы можно увидеть прямо в коде
Выберите синтаксис и инструмент
На Linux чаще встречаются два подхода:
- NASM — Intel-подобный синтаксис, удобен для учебных примеров;
- GAS — assembler из GNU binutils, часто используется вместе с GCC.
Новичку проще начать с NASM. В нем код читается ближе к тому, что обычно показывают в учебниках по x86-64: mov rax, 1, mov rdi, 1, syscall. GAS тоже полезен, но у него другой стиль записи, особенно если используется AT&T-синтаксис
Установите инструменты
Для Debian или Ubuntu:
sudo apt install nasm binutils gcc gdb
Проверьте:
nasm -v
ld -v
gcc --version
gdb --version
nasm нужен для сборки .asm, ld — для linking, gcc удобно использовать, когда ассемблерная функция вызывается из C, gdb нужен для отладки
Первый пример без C-библиотеки
Создайте hello.asm:
section .data
msg db "Linux asm", 10
len equ $ - msg
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, len
syscall
mov rax, 60
xor rdi, rdi
syscall
Сборка:
nasm -f elf64 hello.asm -o hello.o
ld hello.o -o hello
./hello
Вывод:
Linux asm
В этом варианте программа общается с ядром напрямую. rax хранит номер системного вызова, rdi, rsi, rdx хранят аргументы. Для write номер 1, для exit номер 60
Второй путь: ассемблерная функция из C
Если вам удобнее видеть привычный main, можно написать функцию на ассемблере и вызвать ее из C
add.asm:
section .text
global add_two
add_two:
mov rax, rdi
add rax, rsi
ret
main.c:
#include <stdio.h>
long add_two(long a, long b);
int main(void) {
printf("%ld\n", add_two(2, 5));
return 0;
}
Сборка:
nasm -f elf64 add.asm -o add.o
gcc main.c add.o -o app
./app
Результат:
7
Такой способ полезен, когда нужно изучать регистры и соглашение вызова, но не хочется вручную писать вывод числа через системные вызовы
Как отлаживать
Запустите:
gdb ./hello
Полезные команды:
break _start
run
info registers
x/16xb &msg
stepi
Команда info registers показывает значения регистров. stepi выполняет одну машинную инструкцию. Это главный плюс ассемблера: можно увидеть, как меняется процессорное состояние после каждой команды
Что не путать
Linux-ассемблер не равен DOSBox/TASM. Если код содержит .model small, int 21h, ah = 09h, это DOS-пример. В Linux x64 вам нужны ELF-файлы, регистры rax, rdi, rsi, rdx и команда syscall
Также не путайте assembler и архитектуру. NASM может собирать код под разные форматы, но пример все равно должен соответствовать платформе. Команда nasm -f elf64 говорит о формате выходного объектного файла, а не исправляет код, написанный под другую операционную систему
Частые ошибки
Запускают .o вместо программы
file.o — объектный файл. Его нужно связать:
ld file.o -o app
Не понимают, где main
Если вы используете _start, C-библиотека не подключается. Если хотите main, собирайте вместе с C через gcc
Берут пример под Windows
Windows x64 использует другое соглашение и другие системные вызовы. Для Linux берите Linux-примеры
Не передают длину строки
Системный вызов write получает адрес и длину. Он не ищет нулевой байт или $
Что почитать дальше по ассемблеру
Если нужен общий маршрут по теме, откройте рубрику ассемблеру. Для соседних задач пригодятся эти разборы:
- Как написать в Linux программу на ассемблере x64: Hello World
- Как начать писать на ассемблере
- Как писать на ассемблере
- Как писать на ассемблере в Visual Studio