Перенос stepik в подпапку
This commit is contained in:
@@ -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 испорчены — сохрани важное заранее.
|
||||
|
||||
142
docs/syscall.md
Normal file
142
docs/syscall.md
Normal file
@@ -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 — сохраняй, если меняешь).
|
||||
@@ -3,21 +3,6 @@
|
||||
{
|
||||
"path": "casm"
|
||||
},
|
||||
{
|
||||
"path": "sum"
|
||||
},
|
||||
{
|
||||
"path": "even_odd"
|
||||
},
|
||||
{
|
||||
"path": "repeat_str"
|
||||
},
|
||||
{
|
||||
"path": "min"
|
||||
},
|
||||
{
|
||||
"path": "is_digit"
|
||||
},
|
||||
{
|
||||
"path": "docs"
|
||||
},
|
||||
|
||||
29
stepik_base/macro/.vscode/launch.json
vendored
Normal file
29
stepik_base/macro/.vscode/launch.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/macro/.vscode/tasks.json
vendored
Normal file
53
stepik_base/macro/.vscode/tasks.json
vendored
Normal file
@@ -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
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
27
stepik_base/macro/macro.asm
Normal file
27
stepik_base/macro/macro.asm
Normal file
@@ -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
|
||||
30
stepik_base/nasm.code-workspace
Normal file
30
stepik_base/nasm.code-workspace
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user