Windows x64, WinAPI и отличия от Linux
Контекст: Windows x64, NASM, Intel. Первый запуск и MessageBox (32-bit) — Первая программа. Linux syscall — архитектура программ и справочник.
Два способа попросить ОС о работе
| Linux (user mode) | Windows (user mode) |
|---|---|
syscall с номером в RAX | вызов функций из DLL (kernel32.dll, ntdll.dll, …) |
аргументы в RDI, RSI, … | Microsoft x64 ABI + таблицы импорта PE |
| исполняемый ELF | исполняемый PE (Portable Executable) |
Программа на ассемблере под Windows не вызывает ядро напрямую: она зовёт WinAPI (или CRT), а те уже обращаются к системе.
Microsoft x64 calling convention
Первые четыре целочисленных или указательных аргумента:
| Порядок | Регистр |
|---|---|
| 1 | RCX |
| 2 | RDX |
| 3 | R8 |
| 4 | R9 |
Далее — стек (справа налево при подготовке). Вещественные аргументы 1–4 — в XMM0–XMM3.
Shadow space (home space): вызывающий резервирует 32 байта на стеке перед call, даже если функция принимает ≤4 аргументов. Это место, куда callee может временно сохранить регистровые аргументы.
Выравнивание: перед call RSP кратен 16 (с учётом того, что call кладёт 8 байт return address).
Возврат:
- целое / указатель —
RAX(иRDXдля 128 бит); float/double—XMM0.
Callee-saved: RBX, RBP, RDI, RSI, R12–R15, XMM6–XMM15 (нижние 128 бит XMM6–15).
Это не System V AMD64 из статьи про C на Linux: порядок регистров другой, есть shadow space.
Импорт функций из DLL
В 32-битном stdcall имена декорированы: _MessageBoxA@16 (16 байт аргументов). В 64-битном C-стиле — без суффикса @N, с ведущим подчёркиванием в зависимости от линкера.
; NASM, Win64, консольная подсистема
default rel
section .data
msg db 'Hello, Windows x64', 13, 10
msg_len equ $ - msg
bytes_written dq 0
section .text
global main
extern GetStdHandle
extern WriteFile
extern ExitProcess
main:
push rbp
mov rbp, rsp
sub rsp, 48 ; shadow + выравнивание + локальные
mov rcx, -11 ; STD_OUTPUT_HANDLE
call GetStdHandle
mov rbx, rax ; handle в rbx (callee-saved)
mov rcx, rbx
lea rdx, [msg]
mov r8, msg_len
lea r9, [bytes_written]
mov qword [rsp+32], 0 ; lpOverlapped = NULL (5-й аргумент в shadow+stack)
call WriteFile
xor ecx, ecx
call ExitProcess
Сборка (пример с Microsoft toolchain):
nasm -f win64 hello.asm -o hello.obj
link hello.obj kernel32.lib /subsystem:console /entry:main
Точка входа может быть main (с CRT) или _start при -nostdlib — как в первой программе.
Сравнение с Linux syscall
Linux sys_write:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, len
syscall
Windows WriteFile — больше параметров, handle консоли через GetStdHandle, другой ABI. Номера и регистры не переносятся между ОС.
Файлы — CreateFile, ReadFile, CloseHandle
Типичная цепочка (логика, не полный листинг):
CreateFileA/W— открыть или создать (RCX= имя,RDX= access,R8= share,R9= security; остальное на стеке).ReadFile/WriteFile— handle вRCX, буфер вRDX, размер вR8, указатель наDWORD«сколько прочитано» вR9.CloseHandle—RCX= handle.
Ошибки — через GetLastError (возвращает код в RAX после вызова API в некоторых обёртках; уточняйте документацию вызываемой функции).
PE и линковка (кратко)
- Секции
.text,.data,.rdataаналогичны ELF по роли. - Таблица импорта подключает
kernel32.dllавтоматически при линковке сkernel32.lib. - Дизассемблирование:
objdumpиз MinGW или dumpbin из MSVC — см. чтение листинга, формат другой, идея та же.
32-бит и 64-бит на Windows
| 32-bit | 64-bit | |
|---|---|---|
| Вызов API | чаще stdcall, аргументы на стеке | регистры + shadow |
| Имена | @N суффикс | без байтового суффикса |
| Регистры | EAX, EBX, … | RAX, RBX, … |
Учебный MessageBox в 7.md — win32 stdcall. Портирование на x64 требует перекладки аргументов в RCX, RDX, R8, R9 и shadow space.
Когда выбирать Windows-трек
- драйверы и Win32-утилиты под заказчиков на Windows;
- отладка чужих
.exe/ DLL; - сравнение с курсом на Linux в одной энциклопедии.
Для кроссплатформенного ядра знаний сначала закрепите Linux x86-64 + NASM (7 в варианте syscall, 12), затем переносите идеи на WinAPI с заменой ABI.
Связанные материалы
- Несколько модулей —
global/externте же, меняется-f win64и библиотеки линкера. - Взаимодействие с C — на Windows используйте
extern "C"и соглашение компилятора MSVC. - Вещественные аргументы в API — SIMD и float.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Полный отказ от высокоуровневых языков нецелесообразен. Поэтому большинство компиляторов поддерживают встроенный ассемблер — механизм вставки ассемблерных инструкций непосредственно в код на C/C++. %macro, %define и %if в NASM — шаблоны инструкций без дублирования исходного текста. Разделение программы на .asm-файлы, global и extern, сборка объектников и линковка в ELF. Вызов ассемблерных функций из C и наоборот — System V AMD64 ABI, выравнивание стека, сборка. Секции ELF, символы, objdump и сопоставление дизассемблирования с исходным NASM-кодом. REP, MOVS, SCAS, STOS, флаг DF и доступ к данным по индексу через таблицу. SSE2 для float и double, регистры XMM, выравнивание; кратко про стек x87 и AVX. Основы ассемблера - синтаксис Intel/AT&T, базовые инструкции и принципы низкоуровневого программирования. Архитектура ассемблерных программ - взаимодействие с ОС, вызовы библиотек и организация низкоуровневого кода. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и команды процессора в ассемблере - регистр команд, переходы и управление потоком исполнения. Команды и подпрограммы в ассемблере - передача параметров, соглашения вызовов и работа со стеком.История ассемблерных языков
Макросы и условная сборка
Несколько модулей и линковка
Взаимодействие с C и C++
Чтение исполняемого файла и листинга
Строковые инструкции и таблицы поиска
Числа с плавающей точкой и SIMD
Основы ассемблера
Архитектура ассемблерных программ
Типы данных и регистры
Управляющие конструкции и команды процессора
Команды и подпрограммы