Перейти до вмісту

Assembly

Матеріал з K2 ERP Wiki Ukraine — База знань з автоматизації та санкцій в Україні

SEO title: Assembly — асемблер, машинний код, регістри, інструкції CPU, x86-64, ARM, RISC-V, NASM, GAS і низькорівневе програмування SEO description: Assembly — Wiki-стаття про мову асемблера як низькорівневу мову програмування, близьку до машинного коду. Розглянуто машинні інструкції, регістри, пам’ять, стек, calling conventions, ABI, x86-64, ARM/AArch64, RISC-V, NASM, MASM, GNU assembler, LLVM assembler, objdump, disassembler, linker, object files, embedded systems, драйвери, reverse engineering, performance, безпеку, тестування, debugging і практичне використання Assembly у сучасній розробці. SEO keywords: Assembly, assembler, асемблер, мова асемблера, машинний код, machine code, x86 assembly, x86-64 assembly, ARM assembly, AArch64 assembly, RISC-V assembly, NASM, MASM, GAS, GNU assembler, LLVM assembler, registers, CPU instructions, stack, memory, calling convention, ABI, linker, object file, disassembler, objdump, embedded systems, низькорівневе програмування, reverse engineering, debugging assembly, performance optimization Alternative to: високорівневі мови без контролю над CPU; чорна скринька компілятора; ручне вгадування продуктивності; embedded-код без розуміння регістрів; debugging без розуміння машинного коду; оптимізація без аналізу інструкцій; системне програмування без знання ABI; reverse engineering без асемблера


Assembly або мова асемблера — це низькорівнева мова програмування, яка описує інструкції процесора у текстовій формі, близькій до машинного коду.

Кожна архітектура процесора має власний набір інструкцій, регістрів і правил. Тому немає одного універсального Assembly для всіх комп’ютерів: є x86/x86-64 assembly, ARM/AArch64 assembly, RISC-V assembly та інші варіанти.

Коротко: Assembly — це мова, де програміст працює майже напряму з процесором: регістрами, пам’яттю, стеком, інструкціями й calling conventions. Це не зручно для великих застосунків, але дуже корисно для системного програмування, embedded, debugging і розуміння того, як працює код “під капотом”.

Intel Software Developer’s Manual описує архітектуру, середовище програмування та повний instruction set reference для Intel 64 і IA-32 процесорів. [1]

Головна ідея

Головна ідея Assembly — дати людині текстовий спосіб записувати машинні інструкції.

Процесор не виконує Python, C#, Go або Swift напряму. У кінці все зводиться до машинного коду — байтів, які CPU розуміє як інструкції.

Assembly є проміжним рівнем:

High-level code → Compiler → Assembly → Assembler → Machine code

Проста аналогія: високорівнева мова каже “відсортуй список”, а Assembly каже процесору “поклади це значення в регістр, порівняй, зроби перехід, запиши в пам’ять”.

Assembly і машинний код

Машинний код — це байти, які виконує процесор.

Assembly — це читабельний текстовий запис цих інструкцій.

Наприклад, для x86-64 можна побачити щось подібне:

mov rax, 1
add rax, 2
ret

Це значно читабельніше, ніж відповідні байти машинного коду.

Assembler перетворює assembly text у object file або machine code.

Assembler

Assembler — програма, яка перетворює Assembly-код у машинний код або object file.

Популярні assemblers:

  • NASM;
  • MASM;
  • GNU assembler або GAS;
  • LLVM assembler;
  • FASM;
  • YASM;
  • platform-specific assemblers.

GNU assembler documentation описує as як user guide для GNU assembler у складі GNU Binutils. [2]

Disassembler

Disassembler — інструмент, який перетворює машинний код назад у assembly-like текст.

Приклади:

  • objdump;
  • llvm-objdump;
  • gdb;
  • lldb;
  • radare2;
  • Ghidra;
  • IDA Free/IDA Pro;
  • Hopper.

Disassembler корисний для:

  • debugging;
  • reverse engineering;
  • аналізу compiler output;
  • malware analysis;
  • performance tuning;
  • вивчення binary.

Object file

Object file — проміжний файл після компіляції або асемблювання.

Він містить:

  • machine code;
  • symbols;
  • relocation information;
  • debug information;
  • sections;
  • metadata.

Потім linker об’єднує object files у executable або library.

Linker

Linker об’єднує object files і libraries у фінальний executable.

Linker вирішує:

  • де лежать functions;
  • як зв’язати symbols;
  • які libraries підключити;
  • які relocations застосувати;
  • як сформувати executable format.

Формати executable/object files:

  • ELF — Linux;
  • PE/COFF — Windows;
  • Mach-O — macOS/iOS.

CPU architecture

Assembly залежить від архітектури CPU.

Приклади:

  • x86;
  • x86-64;
  • ARM;
  • AArch64;
  • RISC-V;
  • MIPS;
  • PowerPC;
  • AVR;
  • 6502;
  • Z80;
  • WebAssembly-like virtual instruction sets.

Код для x86-64 не запуститься напряму на ARM без перекомпіляції або емуляції.

Instruction Set Architecture

Instruction Set Architecture або ISA — набір правил, які визначають, які інструкції підтримує процесор.

ISA описує:

  • registers;
  • instruction formats;
  • memory addressing;
  • data types;
  • privilege levels;
  • exceptions;
  • calling conventions частково через ABI;
  • system instructions;
  • vector instructions.

Arm documentation описує A64 як instruction set, used in AArch64 and supported by Armv8-A, Armv8-R AArch64 and Armv9-A architectures. [3]

x86 і x86-64

x86 — історична архітектура Intel/AMD.

x86-64 — 64-бітне розширення, яке широко використовується на desktop, laptop і server системах.

x86-64 має:

  • general-purpose registers;
  • SIMD/vector instructions;
  • stack;
  • calling conventions;
  • complex instruction set;
  • legacy modes;
  • operating system support.

Intel manual Volume 2 містить повний instruction set reference для Intel 64 і IA-32. [4]

ARM і AArch64

ARM — архітектура, дуже поширена в мобільних пристроях, embedded, Apple Silicon, servers і IoT.

AArch64 — 64-бітний execution state для Arm.

A64 — instruction set для AArch64.

Arm A-profile A64 documentation надає HTML-опис A64 instruction set architecture. [5]

ARM/AArch64 важливий для:

  • iPhone;
  • Android;
  • Apple Silicon Mac;
  • embedded systems;
  • microcontrollers;
  • cloud ARM servers;
  • low-power devices.

RISC-V

RISC-V — open standard instruction set architecture.

RISC-V важливий для:

  • освіти;
  • досліджень;
  • embedded;
  • custom processors;
  • open hardware;
  • experimentation;
  • academic CPU design.

RISC-V Assembly Programmer’s Manual описує стандартну RISC-V assembly language, яку підтримують GNU as і LLVM assembler. [6]

CISC і RISC

CISC — Complex Instruction Set Computer.

RISC — Reduced Instruction Set Computer.

Спрощено:

Підхід Ідея
CISC складніші інструкції, історично більше можливостей в одній інструкції
RISC простіші інструкції, регулярніший набір операцій

x86 часто вважають CISC-архітектурою.

ARM і RISC-V — RISC-напрям.

У сучасних CPU ця різниця складніша, бо внутрішня реалізація може відрізнятися від зовнішньої ISA.

Registers

Register — дуже швидке місце зберігання всередині CPU.

Приклади x86-64 registers:

  • RAX;
  • RBX;
  • RCX;
  • RDX;
  • RSI;
  • RDI;
  • RSP;
  • RBP;
  • R8–R15.

Приклади AArch64 registers:

  • X0–X30;
  • SP;
  • PC;
  • V0–V31.

Регістрів мало, тому Assembly-код має уважно працювати з ними.

Практична думка: у високорівневій мові ви створюєте змінні майже без обмежень. В Assembly ви постійно думаєте: “у якому регістрі це лежить?” і “чи не перезапишу я потрібне значення?”.

Memory

Assembly працює з пам’яттю напряму або через адреси.

У коді часто зустрічаються:

  • load;
  • store;
  • address;
  • pointer;
  • offset;
  • stack;
  • heap;
  • alignment;
  • memory access size.

Помилки з пам’яттю можуть бути дуже серйозними:

  • crash;
  • data corruption;
  • undefined behavior;
  • security vulnerability;
  • неправильний результат.

Stack

Stack — область пам’яті для function calls, local variables, return addresses і тимчасових даних.

Stack зазвичай росте вниз у пам’яті, але це залежить від архітектури й ABI.

У x86-64 register rsp часто вказує на верх stack.

Stack використовується для:

  • return address;
  • local variables;
  • saved registers;
  • function arguments, якщо не вистачає registers;
  • stack frames.

Stack frame

Stack frame — частина stack, яка належить конкретному function call.

Вона може містити:

  • return address;
  • old frame pointer;
  • local variables;
  • saved registers;
  • temporary storage.

Debuggers часто використовують stack frames, щоб показати call stack.

Calling convention

Calling convention — правила виклику функцій.

Вона визначає:

  • які arguments ідуть у registers;
  • які arguments ідуть у stack;
  • де повертається result;
  • які registers зберігає caller;
  • які registers зберігає callee;
  • як вирівнюється stack;
  • як обробляються variadic functions.

Calling convention залежить від платформи.

Наприклад, x86-64 Windows і x86-64 Linux мають різні calling conventions.

ABI

ABI — Application Binary Interface.

ABI описує binary-level правила взаємодії між compiled modules.

ABI включає:

  • calling convention;
  • object file format;
  • type layout;
  • name mangling;
  • exception handling;
  • dynamic linking;
  • system call interface.

Якщо ABI порушено, код може компілюватися, але падати або працювати неправильно.

Endianness

Endianness — порядок байтів у багатобайтових числах.

Типові варіанти:

  • little-endian;
  • big-endian.

x86-64 зазвичай little-endian.

Endianness важливий для:

  • binary formats;
  • network protocols;
  • embedded systems;
  • file parsing;
  • reverse engineering;
  • interoperability.

Alignment

Alignment — вирівнювання даних у пам’яті.

Деякі CPU працюють швидше або навіть вимагають, щоб дані були вирівняні за певними адресами.

Alignment важливий для:

  • performance;
  • SIMD;
  • ABI;
  • structs;
  • embedded systems;
  • binary compatibility.

Instructions

Instruction — команда CPU.

Приклади типів інструкцій:

  • move/load/store;
  • arithmetic;
  • logic;
  • compare;
  • branch/jump;
  • call/return;
  • stack operations;
  • SIMD/vector operations;
  • system instructions;
  • atomic operations.

Приклади x86-like інструкцій:

mov
add
sub
cmp
jmp
call
ret

Приклади AArch64-like інструкцій:

mov
add
ldr
str
b
bl
ret

Addressing modes

Addressing mode — спосіб вказати, де знаходиться operand.

Приклади:

  • immediate value;
  • register;
  • memory address;
  • register + offset;
  • base + index;
  • PC-relative addressing.

Addressing modes сильно залежать від ISA.

Branching

Branching — умовні або безумовні переходи.

Приклад ідеї:

cmp rax, 0
je zero_case
jmp done

Branches потрібні для:

  • if;
  • loops;
  • switch;
  • function calls;
  • error handling;
  • state machines.

Сучасні CPU мають branch prediction, тому pattern переходів може впливати на performance.

Loops

Цикли в Assembly зазвичай будуються через labels і conditional jumps.

Приклад ідеї:

loop_start:
    ; тіло циклу
    dec rcx
    jnz loop_start

Compiler із високорівневої мови сам генерує подібні конструкції.

System calls

System call — звернення програми до operating system kernel.

System calls потрібні для:

  • файлів;
  • процесів;
  • пам’яті;
  • мережі;
  • вводу/виводу;
  • таймерів;
  • permissions.

System call interface залежить від OS і architecture.

Наприклад, Linux x86-64 і Windows x86-64 мають різні механізми виклику системних функцій.

Inline assembly

Inline assembly — вставка Assembly-коду в C/C++ або іншу мову.

Це може бути корисно для:

  • спеціальних CPU instructions;
  • performance-critical fragments;
  • embedded;
  • low-level control;
  • compiler barriers;
  • hardware access.

Але inline assembly має ризики:

  • важко переносити;
  • легко порушити ABI;
  • compiler optimizations можуть взаємодіяти неочікувано;
  • складно тестувати;
  • складно підтримувати.

Intrinsics

Intrinsics — функції компілятора, які дають доступ до спеціальних CPU instructions без написання сирого Assembly.

Наприклад, SIMD intrinsics дозволяють використовувати vector instructions через C/C++ API.

Intrinsics часто кращі за inline assembly, бо компілятор краще розуміє код і може оптимізувати його.

SIMD і vector instructions

SIMD — Single Instruction, Multiple Data.

SIMD instructions виконують одну операцію над кількома елементами одночасно.

Приклади наборів:

  • SSE;
  • AVX;
  • AVX2;
  • AVX-512;
  • NEON;
  • SVE;
  • RISC-V Vector extension.

SIMD корисний для:

  • audio/video;
  • image processing;
  • cryptography;
  • numerical computing;
  • ML inference;
  • compression;
  • signal processing.

Atomic operations

Atomic operations — операції, які виконуються неподільно щодо інших threads.

Вони потрібні для:

  • concurrency;
  • locks;
  • lock-free data structures;
  • reference counters;
  • synchronization;
  • kernel code.

Atomic assembly instructions складні, бо треба враховувати memory ordering і CPU architecture.

Memory model

Memory model описує правила видимості операцій із пам’яттю між threads або cores.

Це важливо для:

  • multithreading;
  • atomics;
  • locks;
  • lock-free programming;
  • compiler optimization;
  • CPU reordering.

Неправильне розуміння memory model може призвести до рідкісних і дуже складних bugs.

Embedded systems

Assembly часто використовується в embedded systems.

Сценарії:

  • startup code;
  • interrupt handlers;
  • bootloaders;
  • direct hardware access;
  • microcontroller initialization;
  • performance-critical routines;
  • power-sensitive code;
  • tiny memory environments.

У embedded часто пишуть основний код на C/C++, а Assembly — тільки там, де потрібен прямий контроль.

Bootloader

Bootloader — код, який запускається дуже рано й готує систему до виконання основної програми або OS.

Bootloader може:

  • налаштувати CPU;
  • підготувати memory;
  • завантажити kernel;
  • перевірити firmware;
  • налаштувати hardware;
  • перейти в інший execution mode.

Bootloaders часто містять Assembly, бо високорівневе runtime-середовище ще не готове.

Kernel і драйвери

Assembly може використовуватися в operating system kernels і drivers.

Сценарії:

  • context switching;
  • interrupt handling;
  • system call entry;
  • low-level CPU setup;
  • synchronization primitives;
  • memory management;
  • hardware-specific code.

Основна частина kernel часто пишеться C/C++ або Rust, але низькорівневі частини можуть потребувати Assembly.

Reverse engineering

Assembly важливий для reverse engineering.

Reverse engineering може використовуватися для:

  • аналізу старих програм;
  • compatibility;
  • malware analysis;
  • security research;
  • debugging binary without source;
  • understanding protocols;
  • firmware analysis.

Для цього використовують disassemblers, debuggers і decompilers.

Важливо: reverse engineering може бути легальним або незаконним залежно від контексту, ліцензій і юрисдикції. Для корпоративної роботи потрібні правила, дозвіл і юридична перевірка.

Debugging Assembly

Assembly знання допомагає в debugging.

Корисно, коли:

  • програма падає в native code;
  • немає source code;
  • треба читати crash dump;
  • треба зрозуміти stack trace;
  • є memory corruption;
  • compiler optimization ускладнює debugging;
  • потрібно перевірити ABI;
  • performance profiler показує hot instruction.

Інструменти:

  • gdb;
  • lldb;
  • WinDbg;
  • Visual Studio debugger;
  • objdump;
  • perf;
  • Intel VTune;
  • Instruments;
  • perfetto;
  • platform-specific debuggers.

Compiler output

Знання Assembly допомагає читати compiler output.

Це корисно для:

  • performance optimization;
  • understanding inlining;
  • checking vectorization;
  • reverse engineering bugs;
  • learning compiler behavior;
  • ABI debugging.

Популярний інструмент для навчання — Compiler Explorer, де можна подивитися Assembly output для C/C++, Rust, Go та інших мов.

Performance optimization

Assembly іноді використовують для performance.

Але сучасні компілятори дуже сильні.

Перед ручною оптимізацією треба:

  1. виміряти performance;
  2. знайти bottleneck;
  3. перевірити algorithm complexity;
  4. оптимізувати data layout;
  5. перевірити compiler flags;
  6. подивитися generated assembly;
  7. розглянути intrinsics;
  8. тільки потім писати hand-written assembly.

Практичний принцип: Assembly — останній інструмент оптимізації, а не перший. Часто швидше допомагає кращий алгоритм або layout даних.

Безпека

Assembly пов’язаний із безпекою, бо працює близько до пам’яті й CPU.

Ризики:

  • buffer overflow;
  • stack corruption;
  • return address overwrite;
  • use-after-free;
  • integer overflow;
  • calling convention mismatch;
  • unsafe system calls;
  • race conditions;
  • side-channel leaks;
  • gadget-based exploitation.

Захист:

  • stack canaries;
  • ASLR;
  • DEP/NX;
  • control-flow integrity;
  • memory-safe languages;
  • sanitizers;
  • fuzzing;
  • code review;
  • static analysis;
  • careful ABI adherence.

Side-channel attacks

Side-channel attack використовує побічні сигнали, наприклад timing або cache behavior.

Assembly-level знання важливе для розуміння таких атак, бо вони часто залежать від CPU, memory access і branch behavior.

У cryptography потрібно уникати data-dependent timing, якщо це може розкрити секрети.

Assembly і malware

Malware analysis часто включає читання Assembly.

Але Assembly сам по собі не є “шкідливою мовою”.

Це просто низькорівневий інструмент.

Його використовують для:

  • embedded;
  • OS kernels;
  • performance;
  • compilers;
  • reverse engineering;
  • security research;
  • legacy systems.

Як і будь-який інструмент, Assembly може використовуватися відповідально або шкідливо.

Assembly і високорівневі мови

Сьогодні більшість програм не пишуть повністю на Assembly.

Зазвичай використовують:

  • C;
  • C++;
  • Rust;
  • Go;
  • C#;
  • Swift;
  • Java;
  • Python;
  • JavaScript;
  • Dart.

Assembly залишається важливим для:

  • розуміння компіляції;
  • низькорівневих фрагментів;
  • performance analysis;
  • debugging;
  • embedded;
  • reverse engineering;
  • runtime systems.

Assembly і C/C++

C і C++ найближчі до Assembly серед масових мов високого рівня.

Вони дозволяють:

  • працювати з pointers;
  • контролювати layout;
  • викликати intrinsics;
  • взаємодіяти з ABI;
  • писати системний код;
  • використовувати inline assembly;
  • компілювати під різні ISA.

Багато Assembly вивчають саме через C/C++ compiler output.

Assembly і Rust

Rust використовується для системного програмування з фокусом на memory safety.

Rust може замінити частину C/C++ там, де раніше могли знадобитися небезпечні низькорівневі підходи.

Але Rust теж компілюється в машинний код і може взаємодіяти з Assembly, intrinsics і ABI.

Assembly і Python/C#/Go/Swift/Dart

Високорівневі мови не виконуються “магічно”.

Вони або:

  • компілюються в machine code;
  • виконуються через VM;
  • JIT-компілюються;
  • викликають native libraries;
  • використовують runtime.

Assembly знання допомагає зрозуміти:

  • чому код повільний;
  • як працює stack;
  • що таке pointer;
  • що робить compiler;
  • чому native extension падає;
  • як працюють CPU caches;
  • чому data layout важливий.

Assembly у бізнесі

У бізнесі Assembly рідко є основною мовою.

Він може бути потрібен для:

  • embedded продуктів;
  • industrial hardware;
  • firmware;
  • high-performance libraries;
  • cryptography;
  • drivers;
  • legacy binary support;
  • reverse engineering;
  • security audit;
  • performance-critical modules.

Для звичайних ERP, CRM, web, mobile або reporting systems Assembly майже ніколи не є правильним вибором.

Assembly і ERP-системи

Assembly не є ERP-системою.

Він не веде облік, не проводить документи, не керує складом і не формує бізнес-звіти.

У контексті K2 ERP Assembly може бути корисним лише опосередковано:

  • розуміння native crash у сторонній бібліотеці;
  • аналіз performance на рівні compiled code;
  • інтеграція з embedded device;
  • driver або low-level connector;
  • reverse engineering старого binary-компонента з дозволом;
  • аудит native dependency.

Але бізнес-логіку ERP майже завжди потрібно писати високорівневою мовою, а не Assembly.

Assembly і тестування

Assembly-код теж потрібно тестувати.

Підходи:

  • unit tests через C harness;
  • integration tests;
  • emulator tests;
  • hardware-in-the-loop;
  • property-based tests;
  • fuzzing;
  • differential testing;
  • comparing with reference implementation;
  • ABI tests;
  • performance tests.

Особливо важливо тестувати edge cases:

  • overflow;
  • alignment;
  • zero-length input;
  • boundary values;
  • calling convention;
  • register preservation;
  • stack alignment.

Assembly і документація

Для Assembly важлива документація.

Потрібно пояснювати:

  • target architecture;
  • assembler syntax;
  • ABI;
  • calling convention;
  • register usage;
  • clobbered registers;
  • assumptions;
  • alignment;
  • supported OS;
  • tested CPUs;
  • build commands;
  • reason for hand-written assembly.

Без документації Assembly-код швидко стає майже нерозбірливим.

Intel syntax і AT&T syntax

Для x86 Assembly є різні синтаксиси.

Intel syntax часто виглядає так:

mov rax, rbx

AT&T syntax часто виглядає так:

movq %rbx, %rax

Відмінності:

  • порядок operands;
  • префікси registers;
  • позначення sizes;
  • immediate values;
  • memory addressing syntax.

GNU assembler історично часто використовує AT&T syntax для x86, але може підтримувати й Intel syntax залежно від режиму.

NASM

NASM — Netwide Assembler, популярний assembler для x86/x86-64.

NASM часто використовують у:

  • навчанні;
  • OSDev;
  • bootloaders;
  • low-level x86 experiments;
  • hand-written assembly modules.

NASM має власний синтаксис і macros.

MASM

MASM — Microsoft Macro Assembler.

Він використовується в Microsoft/Windows ecosystem.

MASM може зустрічатися в:

  • Windows low-level code;
  • legacy projects;
  • Visual Studio workflows;
  • x86/x64 assembly для Windows;
  • driver-related старих матеріалах.

GNU assembler

GNU assembler або GAS — assembler у складі GNU Binutils.

Він часто використовується в Linux/Unix toolchains.

GNU assembler documentation зазначає, що це user guide для as GNU Binutils. [7]

GAS підтримує багато architectures і часто використовується compiler toolchains.

LLVM assembler

LLVM toolchain має власні assembler/disassembler можливості.

LLVM використовується в:

  • Clang;
  • Swift compiler ecosystem;
  • Rust compiler backend historically via LLVM;
  • many modern language toolchains;
  • cross-compilation;
  • optimization pipelines.

LLVM assembly і machine code tooling важливі для сучасних компіляторів.

WebAssembly

WebAssembly або Wasm — binary instruction format для portable execution.

WebAssembly не є класичним CPU Assembly, але концептуально близький: це низькорівневий instruction format для stack-based virtual machine.

Wasm використовується для:

  • web;
  • sandboxed execution;
  • plugins;
  • edge computing;
  • cross-language runtime;
  • portable modules.

Не варто плутати WebAssembly з x86 або ARM Assembly: Wasm виконується через runtime, а не напряму CPU як native ISA.

Коли Assembly особливо корисний

Assembly особливо корисний для:

  • embedded systems;
  • bootloaders;
  • kernels;
  • drivers;
  • compiler/runtime development;
  • reverse engineering;
  • malware analysis;
  • performance-critical routines;
  • SIMD optimization;
  • ABI debugging;
  • crash dump analysis;
  • security research;
  • навчання computer architecture;
  • low-level hardware access.

Коли Assembly може бути невдалим вибором

Assembly може бути невдалим вибором, якщо:

  • потрібен web app;
  • потрібна ERP-бізнес-логіка;
  • потрібен backend API;
  • потрібна мобільна розробка;
  • потрібна швидка підтримуваність;
  • команда не має low-level досвіду;
  • продуктивність ще не вимірювали;
  • задачу можна вирішити C/Rust/Go;
  • потрібна portability;
  • потрібна безпечна робота з пам’яттю.

Типові помилки в Assembly

Поширені помилки:

  • порушити calling convention;
  • не зберегти callee-saved registers;
  • зламати stack alignment;
  • переплутати operand order;
  • неправильно порахувати offset;
  • не врахувати endianness;
  • зробити out-of-bounds memory access;
  • забути про sign extension;
  • переплутати instruction size;
  • не врахувати ABI;
  • не тестувати edge cases;
  • оптимізувати без вимірювання;
  • не документувати register usage;
  • писати Assembly там, де достатньо C або Rust.

Хороші практики

Під час роботи з Assembly варто:

  1. Чітко вказувати target architecture.
  2. Вказувати assembler і syntax.
  3. Читати офіційний ISA manual.
  4. Дотримуватися ABI і calling convention.
  5. Документувати register usage.
  6. Писати мінімальні Assembly-фрагменти.
  7. Мати reference implementation високого рівня.
  8. Покривати код тестами.
  9. Перевіряти stack alignment.
  10. Використовувати debugger і disassembler.
  11. Вимірювати performance до оптимізації.
  12. Розглядати intrinsics перед hand-written assembly.
  13. Уникати зайвої “магії”.
  14. Писати коментарі не “що”, а “чому”.
  15. Не використовувати Assembly без реальної потреби.

Практичний висновок

Assembly — це важлива низькорівнева мова, яка дозволяє зрозуміти й контролювати роботу процесора.

Сильні сторони:

  • прямий контроль над CPU;
  • робота з registers;
  • розуміння machine code;
  • embedded і firmware;
  • kernels і drivers;
  • debugging native crashes;
  • reverse engineering;
  • performance analysis;
  • SIMD optimization;
  • навчання computer architecture.

Обмеження:

  • низька продуктивність розробника;
  • складність підтримки;
  • залежність від architecture;
  • ризик memory bugs;
  • складність тестування;
  • складність portability;
  • сучасні компілятори часто оптимізують краще;
  • для бізнес-систем майже завжди краще високорівнева мова.

Assembly найкраще використовувати як точний інструмент для специфічних низькорівневих задач, а не як універсальну мову для застосунків.

Пояснення термінів

  • Assembly — низькорівнева мова, що текстово описує інструкції CPU.
  • Assembler — програма, яка перетворює Assembly у machine code або object file.
  • Machine code — байти інструкцій, які виконує процесор.
  • Disassembler — інструмент, що перетворює binary code у assembly-like текст.
  • Instruction — команда процесора.
  • ISA — Instruction Set Architecture, набір інструкцій і правил CPU.
  • Register — швидке сховище всередині CPU.
  • Stack — область пам’яті для function calls і local data.
  • Stack frame — частина stack для конкретного виклику функції.
  • Calling convention — правила передачі arguments і результатів між функціями.
  • ABI — binary-level правила сумісності між модулями.
  • Object file — проміжний файл із machine code і symbols.
  • Linker — інструмент, що об’єднує object files у executable.
  • x86 — історична архітектура Intel/AMD.
  • x86-64 — 64-бітна архітектура Intel/AMD.
  • ARM — поширена RISC-архітектура для mobile, embedded і servers.
  • AArch64 — 64-бітний execution state ARM.
  • A64 — instruction set для AArch64.
  • RISC-V — open standard instruction set architecture.
  • Endianness — порядок байтів у багатобайтових значеннях.
  • Alignment — вирівнювання даних у пам’яті.
  • SIMD — виконання однієї операції над кількома даними.
  • Inline assembly — Assembly-код, вставлений у код C/C++ або іншої мови.
  • Intrinsic — compiler-provided функція для спеціальних CPU інструкцій.
  • System call — звернення програми до kernel.
  • Bootloader — ранній код запуску системи.
  • Reverse engineering — аналіз binary або системи для розуміння її роботи.
  • WebAssembly — portable binary instruction format для sandboxed runtime.

Дивіться також

Джерела