From 444a2da5a83ff55453cf9e9145203c135f9b77ac 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: Mon, 22 Sep 2025 00:53:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=B2=D1=83=D1=85=20=D1=87=D0=B8=D1=81=D0=B5?= =?UTF-8?q?=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- casm/.vscode/launch.json | 2 +- casm/asm.asm | 9 +-- casm/input.txt | 2 + docs/cdecl.md | 55 +++++++++++++ minimal/.vscode/launch.json | 22 +---- minimal/.vscode/tasks.json | 24 +++++- minimal/minimal.asm | 155 +++++++++++++++++++++++++++++++++--- 7 files changed, 230 insertions(+), 39 deletions(-) create mode 100644 casm/input.txt create mode 100644 docs/cdecl.md diff --git a/casm/.vscode/launch.json b/casm/.vscode/launch.json index 527b37d..776c494 100644 --- a/casm/.vscode/launch.json +++ b/casm/.vscode/launch.json @@ -30,7 +30,7 @@ "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true - } + }, ] } ] diff --git a/casm/asm.asm b/casm/asm.asm index 140cd14..9eac30d 100644 --- a/casm/asm.asm +++ b/casm/asm.asm @@ -4,7 +4,7 @@ extern a section .text main: - push rbx ; сохранить rbx + push rbx call scan ; прочитать первое число mov rbx, rax ; сохранить первое число call scan ; прочитать второе число @@ -15,10 +15,9 @@ main: push rax call print ; напечатать pop rax - pop rbx ; восстановить rbx + pop rbx mov dword [rel a], eax ; записать значение 42 в переменную a - mov rdi, 0 - mov rax, 60 - syscall \ No newline at end of file + mov rax, 0 + ret \ No newline at end of file diff --git a/casm/input.txt b/casm/input.txt new file mode 100644 index 0000000..3b86147 --- /dev/null +++ b/casm/input.txt @@ -0,0 +1,2 @@ +10 +20 diff --git a/docs/cdecl.md b/docs/cdecl.md new file mode 100644 index 0000000..2c05e90 --- /dev/null +++ b/docs/cdecl.md @@ -0,0 +1,55 @@ +## System V AMD64 ABI (cdecl): шпаргалка + +Кратко о соглашении вызова для x86-64 в Unix-подобных ОС (Linux, macOS, BSD) по ABI System V. + +### Быстрые правила +- Аргументы (целые/указатели): RDI, RSI, RDX, RCX, R8, R9; остальные — на стек (справа налево). +- Аргументы с плавающей точкой/векторы: XMM0–XMM7; остальные — на стек. +- Возврат значения: целые/указатели — RAX (до 128 бит: RAX/RDX); float/double — XMM0 (до двух — XMM0/XMM1). +- Перед call: RSP должен быть выровнен по 16 байт. Внутри функции на входе RSP % 16 == 8 (из-за return address). +- Red zone: 128 байт под RSP доступны leaf-функциям; не трогается ОС и обработчиками сигналов. +- Нет «shadow space» (он есть только в Windows x64 ABI). + +### Сохранность регистров +- Caller-saved (clobbered): RAX, RCX, RDX, RSI, RDI, R8–R11, XMM0–XMM15, флаги RFLAGS. +- Callee-saved (надо сохранять и восстанавливать в функции): RBX, RSP, RBP, R12–R15. + +### Порядок передачи аргументов +- 1–6-й целочисленные/указательные: RDI, RSI, RDX, RCX, R8, R9. +- Вещественные/векторные: XMM0–XMM7 (независимо от целочисленных). +- 7-й и далее, а также переполнение по типам — через стек. Компиляторы обычно размещают их с выравниванием по 8/16. + +### Вариативные функции (printf и т.п.) +- Перед вызовом variadic-функции младший байт RAX (AL) содержит число использованных XMM-регистров для передачи аргументов с плавающей точкой (0, если их нет). + +### Структуры и большие значения +- Небольшие структуры (до 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 испорчены — сохрани важное заранее. +- В своей функции сохраняй/восстанавливай RBX, RSP, RBP, R12–R15. + + + diff --git a/minimal/.vscode/launch.json b/minimal/.vscode/launch.json index cc80f6f..cbf498a 100644 --- a/minimal/.vscode/launch.json +++ b/minimal/.vscode/launch.json @@ -15,27 +15,7 @@ "request": "launch", "program": "${workspaceFolder}/build/minimal", "cwd": "${workspaceFolder}/build", - "preLaunchTask": "asm64", - - }, - { - "name": "by-gdb x64", - "type": "by-gdb", - "request": "launch", - "program": "${workspaceFolder}/build/minimal", - "cwd": "${workspaceFolder}/build", - "preLaunchTask": "asm64", - - - }, - { - "name": "gdb x64", - "type": "gdb", - "request": "launch", - "program": "${workspaceFolder}/build/minimal", - "preLaunchTask": "asm64", - - + "preLaunchTask": "asm64+gcc" } ] } \ No newline at end of file diff --git a/minimal/.vscode/tasks.json b/minimal/.vscode/tasks.json index 7f4beed..645fd7a 100644 --- a/minimal/.vscode/tasks.json +++ b/minimal/.vscode/tasks.json @@ -9,7 +9,7 @@ "mkdir -p $builddir;", "rawfilename=$builddir/minimal;", "nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;", - "gcc -g -o $rawfilename $rawfilename.o;" + "ld -g -o $rawfilename $rawfilename.o;" ], "problemMatcher": { "pattern": { @@ -26,6 +26,28 @@ "kind": "build", "isDefault": true } + }, + { + "label": "asm64+gcc", + "type": "shell", + "command": [ + "builddir=${workspaceFolder}/build;", + "mkdir -p $builddir;", + "rawfilename=$builddir/minimal;", + "nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.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/minimal/minimal.asm b/minimal/minimal.asm index b1d8214..63163f0 100644 --- a/minimal/minimal.asm +++ b/minimal/minimal.asm @@ -1,20 +1,153 @@ -global _start +global main + +section .data +buff times 256 db 0 +buffend: +count dq 0 section .text -_start: - ; Передаём два числа для суммирования - mov rsi, 12 - mov rdi, 7 - call calculate_sum +default rel - ; Используем результат +main: + call read_text + + lea rsi, [rel buff] ; поставить указатель на начало буфера + call read_num mov rbx, rax + inc rsi + call read_num + mov rdi, rax + mov rsi, rbx - ; Завершение программы - mov rax, 60 - syscall + call calculate_sum + call print_num_to_buf + + mov rdx, [count] + call write_text -; Функция складывает два значения, переданных в RDI и RSI + mov rax, 0 + ret + +; Функция складывает два значения +; In: RDI, RSI +; Out: RAX calculate_sum: lea rax, [rdi + rsi] + ret + +; Функция читает текст в буффер +; Регистры не изменяет +read_text: + push rax + push rdi + push rsi + push rdx + + mov rax, 0 ; sys_read + mov rdi, 0 ; stdin + lea rsi, [rel buff] ; адрес буфера + mov rdx, 256 ; количество байт + syscall + mov [count], rax + + pop rdx + pop rsi + pop rdi + pop rax + ret + +; Функция выводит buff +; In: +; - RSI - начало буфера +; - RDX - размер буфера +; Регистры не изменяет +write_text: + push rax + push rdi + push rsi + push rdx + + mov rax, 1 ; sys_write + mov rdi, 1 ; stdout + syscall + mov [count], rax + + pop rdx + pop rsi + pop rdi + pop rax + ret + + +; Функция читает число из stdin +; Out: RAX +read_num: + push rbx + push rcx + xor rbx, rbx ; обнулить регистр для символов + xor rax, rax ; обнулить аккумулирующий регистр + mov rcx, 10 ; множитель для сдвига + + lea rdi, [rel buff] ; rdi - конец данных + add rdi, [count] + + .while: + cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами + jge .return + + mov bl, [rsi] ; прочитать текущий символ + + cmp bl, 0xA ; если конец строки (\n) - закончить + je .return + cmp bl, 0 ; если null (\0) - закончить + jz .return + cmp bl, ' ' ; если пробел - закончить + jz .return + + mul rcx + and bl, 0x0F + add rax, rbx + inc rsi + jmp .while + +.return: + pop rcx + pop rbx + ret + + +; Функция выводит число в buff +; In: RAX +; Out: RSI - указатель на начало строки +print_num_to_buf: + lea rsi, [buffend] + dec rsi + + mov rdi, rsi ; сохранить последний адрес + mov byte [rsi], 0 ; вписать нуль + dec rsi + mov byte [rsi], 0xA ; вписать перенос строки + dec rsi + mov byte [rsi], '0' + + test rax, rax + jz .return + + mov rcx, 10 ; десятичный делитель + + .while: + xor rdx, rdx + test rax, rax + jz .break + div rcx + or dl, 0x30 + mov [rsi], dl + dec rsi + jmp .while +.break: + inc rsi +.return: + sub rdi, rsi + inc rdi + mov [count], rdi ret \ No newline at end of file