Ассемблер
Как работать на ассемблере
Работать на ассемблере значит писать .asm-файлы, собирать их подходящим assembler, связывать через linker, запускать программу и проверять результат в отладчике. В отличие от языков высокого уровня, здесь нужно внимательно следить за регистрами, памятью, флагами, стеком и платформой
Рабочий процесс
Практический цикл такой:
- написать небольшой фрагмент кода;
- собрать
.asmв.oили.obj; - связать программу;
- запустить;
- проверить вывод или состояние регистров;
- исправить ошибку;
- повторить.
Для Linux x64:
nasm -f elf64 app.asm -o app.o
ld app.o -o app
./app
Для Visual Studio и MASM работа чаще идет внутри C++ проекта. Для TASM нужен DOSBox и команды tasm, tlink
Как организовать файлы
Держите проект просто:
project/
app.asm
notes.md
build.sh
В notes.md удобно записывать, что делает программа, какие регистры используются и какой командой она собирается. Для маленьких учебных задач это кажется лишним, но уже через неделю такие заметки экономят время
Как проверять код
Ассемблерный код проверяют не только выводом на экран. Часто нужно смотреть регистры и память
В GDB:
info registers
stepi
x/16xb $rsp
info registers показывает регистры. stepi выполняет одну инструкцию. x/16xb $rsp показывает байты около стека
Как работать с ошибками
Ошибки обычно относятся к одному из уровней:
- assembler не понял синтаксис;
- linker не нашел символ;
- программа запускается, но логика неверная;
- пример написан под другую платформу.
Если ошибка на этапе сборки, смотрите строку и синтаксис. Если ошибка на этапе linking, смотрите имена функций, global, extern и объектные файлы. Если программа запускается, но результат неверный, смотрите регистры и флаги
Как работать с C
На практике ассемблер часто используют не как отдельную большую программу, а как маленькую функцию рядом с C или C++
Пример идеи:
global add_two
add_two:
mov rax, rdi
add rax, rsi
ret
Здесь важно знать соглашение вызова: в Linux x86-64 первые аргументы идут через rdi, rsi, rdx, rcx, r8, r9. В Windows x64 порядок другой: rcx, rdx, r8, r9
Как не ломать программу
Следите за регистрами, которые обязаны сохраняться по соглашению вызова. Если функция меняет callee-saved регистр и не восстанавливает его, ошибка может проявиться далеко не сразу
Также следите за стеком. Если вы сделали push, должен быть соответствующий pop или корректное восстановление rsp. Неправильный стек может сломать ret
Где ассемблер полезен
Ассемблер используют для:
- чтения дизассемблированного кода;
- отладки низкоуровневых ошибок;
- понимания компилятора;
- микроконтроллеров;
- загрузчиков;
- отдельных критичных фрагментов;
- обучения архитектуре процессора.
Писать большую программу целиком на ассемблере сегодня нужно редко. Но понимать его полезно даже разработчику на C, C++, Rust или Go
Частые ошибки
Работать без отладчика
Без регистров и памяти вы не видите, что происходит
Не различать синтаксисы
NASM, MASM, GAS и TASM пишутся по-разному
Переносить код между Linux и Windows без правок
Системные вызовы и соглашения вызова отличаются
Что почитать дальше по ассемблеру
Если нужен общий маршрут по теме, откройте рубрику ассемблеру. Для соседних задач пригодятся эти разборы:
- CMP в ассемблере
- CMP в ассемблере: как работает сравнение
- CMP, JMP, JE, JNZ: условия и циклы в ассемблере
- CPI в ассемблере: что делает инструкция