From 184eeb85250d978e97f7834cd1af1983e8626960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=8B=D1=82=D0=BA=D0=BE=D0=B2=20=D0=A0=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Tue, 23 Sep 2025 17:48:53 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81=20st?= =?UTF-8?q?epik=20=D0=B2=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B0=D0=BF=D0=BA?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cdecl.md | 21 +-- docs/syscall.md | 142 ++++++++++++++++++ nasm.code-workspace | 15 -- .../even_odd}/.vscode/launch.json | 0 .../even_odd}/.vscode/tasks.json | 0 .../even_odd}/even_odd.asm | 0 .../is_digit}/.vscode/launch.json | 0 .../is_digit}/.vscode/tasks.json | 0 .../is_digit}/is_digit.asm | 0 stepik_base/macro/.vscode/launch.json | 29 ++++ stepik_base/macro/.vscode/tasks.json | 53 +++++++ stepik_base/macro/macro.asm | 27 ++++ {min => stepik_base/min}/.vscode/launch.json | 0 {min => stepik_base/min}/.vscode/tasks.json | 0 {min => stepik_base/min}/min.asm | 0 stepik_base/nasm.code-workspace | 30 ++++ .../repeat_str}/.vscode/launch.json | 0 .../repeat_str}/.vscode/tasks.json | 0 .../repeat_str}/repeat_str.asm | 0 {sum => stepik_base/sum}/.vscode/launch.json | 0 {sum => stepik_base/sum}/.vscode/tasks.json | 0 {sum => stepik_base/sum}/sum.asm | 0 22 files changed, 282 insertions(+), 35 deletions(-) create mode 100644 docs/syscall.md rename {even_odd => stepik_base/even_odd}/.vscode/launch.json (100%) rename {even_odd => stepik_base/even_odd}/.vscode/tasks.json (100%) rename {even_odd => stepik_base/even_odd}/even_odd.asm (100%) rename {is_digit => stepik_base/is_digit}/.vscode/launch.json (100%) rename {is_digit => stepik_base/is_digit}/.vscode/tasks.json (100%) rename {is_digit => stepik_base/is_digit}/is_digit.asm (100%) create mode 100644 stepik_base/macro/.vscode/launch.json create mode 100644 stepik_base/macro/.vscode/tasks.json create mode 100644 stepik_base/macro/macro.asm rename {min => stepik_base/min}/.vscode/launch.json (100%) rename {min => stepik_base/min}/.vscode/tasks.json (100%) rename {min => stepik_base/min}/min.asm (100%) create mode 100644 stepik_base/nasm.code-workspace rename {repeat_str => stepik_base/repeat_str}/.vscode/launch.json (100%) rename {repeat_str => stepik_base/repeat_str}/.vscode/tasks.json (100%) rename {repeat_str => stepik_base/repeat_str}/repeat_str.asm (100%) rename {sum => stepik_base/sum}/.vscode/launch.json (100%) rename {sum => stepik_base/sum}/.vscode/tasks.json (100%) rename {sum => stepik_base/sum}/sum.asm (100%) diff --git a/docs/cdecl.md b/docs/cdecl.md index 2c05e90..63f7fb3 100644 --- a/docs/cdecl.md +++ b/docs/cdecl.md @@ -15,6 +15,7 @@ - Callee-saved (надо сохранять и восстанавливать в функции): RBX, RSP, RBP, R12–R15. ### Порядок передачи аргументов +#### Для call - 1–6-й целочисленные/указательные: RDI, RSI, RDX, RCX, R8, R9. - Вещественные/векторные: XMM0–XMM7 (независимо от целочисленных). - 7-й и далее, а также переполнение по типам — через стек. Компиляторы обычно размещают их с выравниванием по 8/16. @@ -26,26 +27,6 @@ - Небольшие структуры (до 16 байт) могут возвращаться через регистры (классы INTEGER/SSE распределяются по RAX/RDX или XMM0/XMM1). - Большие структуры возвращаются через скрытый указатель: вызывающий передаёт адрес буфера (sret), а функция заполняет его. -### Типичный каркас функции на NASM (Linux) -; extern printf -; global main -; section .data -; msg db "x=%d", 10, 0 -; section .text -; main: -; push rbp -; mov rbp, rsp -; ; на входе rsp%16==8 → для вызова нужно выровнять до 16 -; sub rsp, 8 ; выравнивание стека перед call -; lea rdi, [rel msg] ; 1-й аргумент (fmt) → RDI -; mov esi, 42 ; 2-й аргумент (int) → ESI -; xor eax, eax ; AL=0: нет XMM-аргументов (variadic правило) -; call printf -; add rsp, 8 ; вернуть выравнивание -; xor eax, eax ; return 0 -; pop rbp -; ret - ### Памятка при написании кода - До call: выровняй стек (RSP%16==0), подготовь регистры аргументов, AL для varargs. - После call: результат смотри в RAX/XMM0; считай, что caller-saved испорчены — сохрани важное заранее. diff --git a/docs/syscall.md b/docs/syscall.md new file mode 100644 index 0000000..e054cdf --- /dev/null +++ b/docs/syscall.md @@ -0,0 +1,142 @@ +## Linux x86-64 syscall: шпаргалка + +Кратко о низкоуровневых системных вызовах в Linux на x86-64 через инструкцию `syscall` (без libc/glibc-обёрток). + +### Быстрые правила +- Номер системного вызова: в RAX. +- Аргументы (целые/указатели): RDI, RSI, RDX, R10, R8, R9 +- Возврат: RAX (неотрицательно при успехе; при ошибке — отрицательное значение `-errno` в диапазоне [-4095..-1]). +- Порченные регистры инструкцией `syscall`: RCX, R11 (всегда). RAX меняется на код возврата. +- Остальные GPR обычно возвращаются неизменёнными ядром, но в пользовательском коде всё равно соблюдай ABI: после syscalls/функций не полагайся на caller-saved (RAX, RCX, RDX, RSI, RDI, R8–R11). +- Выравнивание стека: сама инструкция `syscall` не требует 16-байтового выравнивания RSP, но если ты в функции, которая также вызывает обычные функции по ABI System V, держи RSP кратным 16 перед `call`. +- Red zone (128 байт под RSP) по ABI доступна и не трогается ядром/обработчиками сигналов — её можно использовать в leaf-коде. Если есть сомнения/нестандартное окружение — не полагайся на неё. + +### Возврат и обработка ошибок +- При успехе RAX содержит неотрицательный результат (часто количество байт, файловый дескриптор и т.п.). +- При ошибке RAX содержит отрицательное число `-errno` (в диапазоне [-4095..-1]). Никаких флагов (CF/ZF) для ошибки не используется — проверяй знак RAX. +- Обёртки libc (например, `write(2)`) конвертируют `-errno` в `-1` и устанавливают `errno`. В чистом `syscall` этого нет — обрабатывай сам. + +### Где взять номера системных вызовов +- Заголовки ядра: `/usr/include/x86_64-linux-gnu/asm/unistd_64.h` (или `/usr/include/asm/unistd_64.h` в зависимости от дистрибутива). +- `man 2 syscall`, `man 2 <имя>` (например, `man 2 write`). Часто используется `openat` вместо устаревающего `open`. + +### Примеры (NASM) + +#### write(1, msg, len) +```nasm +; ssize_t write(int fd, const void *buf, size_t count) +; x86-64: rax=1 (SYS_write), rdi=fd, rsi=buf, rdx=count + mov rax, 1 ; SYS_write + mov rdi, 1 ; fd = stdout + lea rsi, [rel msg] ; buf + mov rdx, msg_end - msg ; count + syscall + ; при успехе: rax = число записанных байт (>=0) + ; при ошибке: rax < 0 ( = -errno ) + +msg: db "Hello, syscall!\n", 0x0A +msg_end: +``` + +#### exit(status) +```nasm +; void _exit(int status) — завершает текущий поток/процесс (exit_group завершит все потоки процесса) + mov rax, 60 ; SYS_exit + xor rdi, rdi ; status = 0 + syscall ; не возвращается +``` + +#### openat(AT_FDCWD, path, flags, mode) +```nasm +; long openat(int dirfd, const char *pathname, int flags, mode_t mode) +; x86-64: rax=257 (SYS_openat), rdi=dirfd, rsi=pathname, rdx=flags, r10=mode + mov rax, 257 ; SYS_openat + mov rdi, -100 ; AT_FDCWD + lea rsi, [rel path] + mov rdx, 0x0002 ; O_RDWR (пример) + mov r10, 0o644 ; mode + syscall + ; rax >= 0 => fd, rax < 0 => -errno + +path: db "./file.txt", 0 +``` + +#### mmap(addr, length, prot, flags, fd, offset) — пример с 6 аргументами +```nasm +; void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) +; x86-64: rax=9, rdi=addr, rsi=length, rdx=prot, r10=flags, r8=fd, r9=offset + mov rax, 9 ; SYS_mmap + xor rdi, rdi ; addr = NULL (hint) + mov rsi, 4096 ; length + mov rdx, 3 ; PROT_READ|PROT_WRITE + mov r10, 0x22 ; MAP_PRIVATE|MAP_ANONYMOUS (пример) + mov r8, -1 ; fd = -1 (анонимно) + xor r9, r9 ; offset = 0 + syscall + ; при успехе: rax = адрес (неотрицательный с точки зрения знака) + ; при ошибке: rax < 0 => -errno +``` + +### Часто используемые номера (x86-64) +- 0 — read +- 1 — write +- 2 — open (лучше использовать 257 — openat) +- 3 — close +- 9 — mmap +- 11 — munmap +- 12 — brk +- 60 — exit +- 61 — wait4 +- 62 — kill +- 63 — uname +- 78 — gettimeofday (устар.; лучше clock_gettime: 228) +- 202 — futex +- 231 — exit_group (завершает все потоки процесса) +- 257 — openat + +Подробный список — в заголовках ядра и `man 2`. + +### Замечания и подводные камни +- Не путай ABI функций и ABI `syscall`. У `syscall` 4-й аргумент — R10 (не RCX), и `syscall` портит RCX/R11. +- Не используй `int 0x80` на x86-64: это 32-битный интерфейс, он не совместим и может вести к неверной работе. +- Проверка ошибок только по знаку RAX, флаги процессора неинформативны. +- Если смешиваешь `syscall` и обычные вызовы функций, следи за выравниванием стека (RSP%16==0 перед `call`). +- В многопоточных программах предпочитай `exit_group(231)` для завершения целого процесса; `exit(60)` завершает только текущий поток. + +### Мини-макрос для NASM (опционально) +```nasm +; Использование: SYS SYS_write, fd, buf, len +%define SYS_read 0 +%define SYS_write 1 +%define SYS_openat 257 +%define SYS_exit 60 + +%macro SYS 1-7 + mov rax, %1 +%if %0 > 1 + mov rdi, %2 +%endif +%if %0 > 2 + mov rsi, %3 +%endif +%if %0 > 3 + mov rdx, %4 +%endif +%if %0 > 4 + mov r10, %5 +%endif +%if %0 > 5 + mov r8, %6 +%endif +%if %0 > 6 + mov r9, %7 +%endif + syscall +%endmacro +``` + +### Памятка при написании кода +- Положи номер вызова в RAX, аргументы — в RDI, RSI, RDX, R10, R8, R9. +- Вызови `syscall`; помни, что RCX и R11 будут испорчены. +- Проверяй RAX: `rax < 0` — ошибка (`-errno`), `rax >= 0` — успех. +- Соблюдай сохранность регистров по ABI System V для своего кода (RBX/RBP/R12–R15 — сохраняй, если меняешь). diff --git a/nasm.code-workspace b/nasm.code-workspace index 999dc31..35a4398 100644 --- a/nasm.code-workspace +++ b/nasm.code-workspace @@ -3,21 +3,6 @@ { "path": "casm" }, - { - "path": "sum" - }, - { - "path": "even_odd" - }, - { - "path": "repeat_str" - }, - { - "path": "min" - }, - { - "path": "is_digit" - }, { "path": "docs" }, diff --git a/even_odd/.vscode/launch.json b/stepik_base/even_odd/.vscode/launch.json similarity index 100% rename from even_odd/.vscode/launch.json rename to stepik_base/even_odd/.vscode/launch.json diff --git a/even_odd/.vscode/tasks.json b/stepik_base/even_odd/.vscode/tasks.json similarity index 100% rename from even_odd/.vscode/tasks.json rename to stepik_base/even_odd/.vscode/tasks.json diff --git a/even_odd/even_odd.asm b/stepik_base/even_odd/even_odd.asm similarity index 100% rename from even_odd/even_odd.asm rename to stepik_base/even_odd/even_odd.asm diff --git a/is_digit/.vscode/launch.json b/stepik_base/is_digit/.vscode/launch.json similarity index 100% rename from is_digit/.vscode/launch.json rename to stepik_base/is_digit/.vscode/launch.json diff --git a/is_digit/.vscode/tasks.json b/stepik_base/is_digit/.vscode/tasks.json similarity index 100% rename from is_digit/.vscode/tasks.json rename to stepik_base/is_digit/.vscode/tasks.json diff --git a/is_digit/is_digit.asm b/stepik_base/is_digit/is_digit.asm similarity index 100% rename from is_digit/is_digit.asm rename to stepik_base/is_digit/is_digit.asm diff --git a/stepik_base/macro/.vscode/launch.json b/stepik_base/macro/.vscode/launch.json new file mode 100644 index 0000000..2825518 --- /dev/null +++ b/stepik_base/macro/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Launch x64", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/build/macro", + "cwd": "${workspaceFolder}/build", + "preLaunchTask": "asm64", + }, + { + "name": "(gdb) Launch x64", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/macro", + "cwd": "${workspaceFolder}/build", + "preLaunchTask": "asm64" + }, + { + "name": "(gdb+gcc) Launch x64", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/macro", + "cwd": "${workspaceFolder}/build", + "preLaunchTask": "asm64+gcc" + } + ] +} \ No newline at end of file diff --git a/stepik_base/macro/.vscode/tasks.json b/stepik_base/macro/.vscode/tasks.json new file mode 100644 index 0000000..a692aa6 --- /dev/null +++ b/stepik_base/macro/.vscode/tasks.json @@ -0,0 +1,53 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "asm64", + "type": "shell", + "command": [ + "builddir=${workspaceFolder}/build;", + "mkdir -p $builddir;", + "rawfilename=$builddir/macro;", + "nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/macro.asm;", + "ld -g -o $rawfilename $rawfilename.o;" + ], + "problemMatcher": { + "pattern": { + "regexp": "error" + } + }, + "presentation": { + "focus": true, + "panel": "dedicated", + "reveal": "silent", + "clear": true + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "asm64+gcc", + "type": "shell", + "command": [ + "builddir=${workspaceFolder}/build;", + "mkdir -p $builddir;", + "rawfilename=$builddir/macro;", + "nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/macro.asm;", + "gcc -o $rawfilename $rawfilename.o;" + ], + "problemMatcher": { + "pattern": { + "regexp": "error" + } + }, + "presentation": { + "focus": true, + "panel": "dedicated", + "reveal": "silent", + "clear": true + }, + } + ] +} \ No newline at end of file diff --git a/stepik_base/macro/macro.asm b/stepik_base/macro/macro.asm new file mode 100644 index 0000000..11eb87a --- /dev/null +++ b/stepik_base/macro/macro.asm @@ -0,0 +1,27 @@ +; Макрос для вывода сообщения +; In: +; - %1 - указатель на буффер с сообщением +; - %2 - длина сообщения +%macro PRINT_MSG 2 + mov rax, 1 ; sys_write + mov rdi, 1 ; stdout + mov rsi, %1 + mov rdx, %2 + syscall +%endmacro + +global _start + +section .data +g_msg db "Hello, Stepic!" +g_msg_len dq $-g_msg + +section .text +default rel + +_start: + PRINT_MSG g_msg, [g_msg_len] + + mov rax, 60 + mov rdi, 0 + syscall diff --git a/min/.vscode/launch.json b/stepik_base/min/.vscode/launch.json similarity index 100% rename from min/.vscode/launch.json rename to stepik_base/min/.vscode/launch.json diff --git a/min/.vscode/tasks.json b/stepik_base/min/.vscode/tasks.json similarity index 100% rename from min/.vscode/tasks.json rename to stepik_base/min/.vscode/tasks.json diff --git a/min/min.asm b/stepik_base/min/min.asm similarity index 100% rename from min/min.asm rename to stepik_base/min/min.asm diff --git a/stepik_base/nasm.code-workspace b/stepik_base/nasm.code-workspace new file mode 100644 index 0000000..087e04f --- /dev/null +++ b/stepik_base/nasm.code-workspace @@ -0,0 +1,30 @@ +{ + "folders": [ + { + "path": "sum" + }, + { + "path": "even_odd" + }, + { + "path": "repeat_str" + }, + { + "path": "min" + }, + { + "path": "is_digit" + }, + { + "path": "macro" + }, + { + "path": "../docs" + }, + ], + "settings": { + "debug.allowBreakpointsEverywhere": true, + "debug.inlineValues": "on", + "editor.codeLens": false, + } +} diff --git a/repeat_str/.vscode/launch.json b/stepik_base/repeat_str/.vscode/launch.json similarity index 100% rename from repeat_str/.vscode/launch.json rename to stepik_base/repeat_str/.vscode/launch.json diff --git a/repeat_str/.vscode/tasks.json b/stepik_base/repeat_str/.vscode/tasks.json similarity index 100% rename from repeat_str/.vscode/tasks.json rename to stepik_base/repeat_str/.vscode/tasks.json diff --git a/repeat_str/repeat_str.asm b/stepik_base/repeat_str/repeat_str.asm similarity index 100% rename from repeat_str/repeat_str.asm rename to stepik_base/repeat_str/repeat_str.asm diff --git a/sum/.vscode/launch.json b/stepik_base/sum/.vscode/launch.json similarity index 100% rename from sum/.vscode/launch.json rename to stepik_base/sum/.vscode/launch.json diff --git a/sum/.vscode/tasks.json b/stepik_base/sum/.vscode/tasks.json similarity index 100% rename from sum/.vscode/tasks.json rename to stepik_base/sum/.vscode/tasks.json diff --git a/sum/sum.asm b/stepik_base/sum/sum.asm similarity index 100% rename from sum/sum.asm rename to stepik_base/sum/sum.asm