Первая программа: структура файла NASM

Урок разбирает скелет программы на NASM: какие секции бывают и где начинается исполнение.

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

Минимальная программа целиком

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

section .text          ; секция с кодом
    global _start      ; делаем метку _start видимой для линкера

_start:                ; точка входа программы
    mov rax, 60        ; номер системного вызова exit
    mov rdi, 0         ; код возврата = 0
    syscall            ; передать управление ядру

Чтобы собрать и запустить её под Linux:

nasm -f elf64 prog.asm -o prog.o   # ассемблировать в объектный файл
ld prog.o -o prog                  # слинковать в исполняемый файл
./prog                             # запустить
echo $?                            # показать код возврата: 0

Три главные секции

Память программы делится на области по назначению:

СекцияЧто хранит
.textсам код — машинные команды (только чтение и исполнение)
.dataзаранее заданные данные: строки, числа-константы
.bssместо под переменные, которые на старте равны нулю

Пример с данными:

section .data
    message db "Hi", 10    ; строка "Hi" и байт 10 (перевод строки)
    number  dq 42          ; 64-битное число 42

section .bss
    buffer resb 64         ; зарезервировать 64 байта под буфер

Здесь db — define byte (по байту), dq — define quad (8 байт), resb — reserve bytes. Метка вроде message — это просто адрес начала данных.

Как работает под капотом

Метка _start особенная: именно с её адреса операционная система начинает исполнение, когда вы запускаете программу через ld напрямую. Если же вы линкуете с библиотекой C, точкой входа становится main, а _start прячется внутри стартового кода libc, который потом вызывает ваш main. Линкер собирает секции в единый образ ELF — формат исполняемых файлов в Linux — и записывает в его заголовок адрес точки входа.

Частые ошибки

  • Забыть global _start. Без этого линкер не найдёт точку входа и выдаст ошибку.
  • Завершать программу через ret в _start. Возвращаться некуда — программу запустило ядро напрямую. Нужен системный вызов exit (rax = 60).
  • Путать .data и .bss. В .data кладут готовые значения, в .bss только резервируют место (оно обнуляется при запуске).

Итог

  • Программа делится на секции: .text (код), .data (данные), .bss (нулевые переменные).
  • Метка _start — точка входа; её делают видимой через global _start.
  • Завершение — через системный вызов exit, а не ret.
  • NASM ассемблирует, ld линкует в исполняемый файл ELF.
Проверьте себя
1. В какой секции хранится сам код программы?
A.data
B.bss
C.text
D.stack
2. Зачем нужна строка global _start?
AЧтобы создать переменную start
BЧтобы линкер увидел точку входа программы
CЧтобы ускорить программу
DЧтобы подключить стандартную библиотеку
3. Почему программу нельзя завершать через ret в _start?
Aret слишком медленная команда
BВозвращаться некуда — программу запустило ядро напрямую, нужен exit
Cret стирает регистры
Dret работает только в .data