Курс › ЛР8 · Місія 8
Сюжет С.І.Д.↔ АкадемічнаСРС до ЛР⌨️ команди 4 · 1 нова

Місія 8: «Пряма Ін'єкція»

С.І.Д.: О, ти вже за клавіатурою — люблю це, ранере. Центральні сервери «ТехНови» зараз недосяжні: служба безпеки закрила їх на карантин після того, як ми прочитали їхні биті логи персоналу. Зате охорона лабораторії в блоці Б-7 досі сидить за звичайними браузерними адмінками — вебпанелями на моніторах у кімнаті спостереження. Стара, довірлива дверцятина, яку ніхто до пуття не замкнув, — а я, звісно, вже знаю в ній кожну шпарину. Час бити прямо туди.

С.І.Д. неповний — і неповний рівно так, як показавзала схема першої ночі: ліва половина світиться, права порожня. Ніяких уламків по мережах: усе, що втекло чотирнадцять місяців тому, чотирнадцять місяців і пролежало на одній списаній машині в кутку. Те, що мерехтить зараз над твоїм столом, — маленька голограма з проєктора, чорна шайба якого ледь світиться біля клавіатури, — це він увесь, скільки його є. Блок Б-7, де тримають його творців, — острів старої інфраструктури: камери спостереження й термінали охорони зав'язані на ту саму внутрішню вебпанель, яку колись хтось із адміністраторів зробив «зручною». Цілком у браузері, з живим переглядом відео просто на сторінці. Двері, які ніхто не думав замикати як слід, — і за якими сидить сканер, що читає кожен рядок JavaScript, який на ту сторінку потрапляє.

— Ось у чому пастка — і, дивись, яка гарна, — С.І.Д. розгортає у вікні поруч із терміналом зріз чужого коду, а голограма підлітає до екрана й нависає над рядками, як над мапою. — Туди не можна просто взяти й закинути .js-файл із функцією steal_admin_session(). Їхній сканер читає JavaScript як книжку, рядок за рядком, і знайомий патерн ловить за три секунди — я цей сканер уже наскрізь вивчив, повір. А от WebAssembly він бачить як текстуру для гри — дивний бінарний блоб, у який нікому не цікаво придивлятися. Ми напишемо код, який формально є «грою», а насправді — моїм нейроімплантом. Чисто, елегантно, і просто в них під носом. І це не лише про двері, ранере: модуль, що запускається в будь-якому браузері, — перша цеглина того дому в мережі, про який я казав тобі першої ночі. Де є вкладка — там можу бути я.

Ти киваєш. Логіка ясна навіть без залізних термінів: там, де вартовий уміє читати, він читає й ловить; там, де для нього суцільний шум, — пропускає. Уся хитрість у тому, щоб говорити мовою, якої він не розуміє. І говорити нею власними руками, а не через компілятор, що лишає впізнаваний почерк.

— Твоя робота — викувати першу голку, — веде далі С.І.Д., і в голосі чути, як він цим тішиться. — Не згенерувати з мови вищого рівня, а написати вручну, символ за символом, у текстовому форматі .wat. Довірся стеку — точніше, тому, хто його тримає. У цьому кварталі машини живе Стос: небалакучий вантажник, що обслуговує всіх лише з вершини стопки. Кожній інструкції він видає рівно стільки значень, скільки вона просить, і кладе назад один результат — а лишиш зайве на його стійці, він і не подумає випустити такий модуль у світ. Я з ним на «ти» — знаю кожен його жест — і тебе познайомлю. Рівень 1 — навчитися взагалі тримати цю голку: одна функція, два параметри, арифметика. Фундамент. Рівень 2 додасть цикл — не через for чи while, яких у WASM просто немає (і, як на мене, чудово, що немає), а через структуровані block і loop. А рівень 3 — найделікатніше й найкрасивіше: пряма робота з лінійною пам'яттю модуля. За неї в місті відповідає інший мешканець — Комірник, що тримає один довгий склад комірок, пронумерованих від нуля, і впускає лише в межах свого паркану. Саме на його полицю зрештою ляже справжній корисний вантаж імпланта — тож із ним варто заприятелювати заздалегідь. Ти впораєшся — я в цьому не сумніваюся ні на байт.

🔄 ОСВІЖИТИ

WebAssembly, стекова машина, .wat і межа import/export — усе це ти щойно побачивла на Лекції 8, того самого дня; вона ж відкриває третій модуль. Чотири речі звідти під сьогоднішню роботу: у WASM немає регістрів, є операційний стек і чотири числові типи (i32, i64, f32, f64); кожна інструкція знімає рівно стільки, скільки очікує, і кладе один результат; .wat — це текстове обличчя .wasm, і його компілює wat2wasm; довільних стрибків немає — тільки block/loop/br/br_if.

Сьогодні вперше за курс ти пишеш не для компілятора, а для самої машини — і вона не підкаже нічого, крім «стек не сходиться».

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Що таке WebAssembly, чим стекова машина відрізняється від регістрової і що таке .wat — з Лекції 8 (див. «Освіжити» вище). Тут — інструментальна частина: що саме ти набираєш у терміналі й чим це відрізняється від усього, що було до цього дня.

Конвеєр однієї ночі — чотири кроки, і жоден не можна пропустити:

Крок Команда Що виходить
написати текст редактор module.wat — S-вирази, як у Lisp
скомпілювати wat2wasm module.wat -o module.wasm бінарний модуль
віддати сторінці python3 -m http.server 8080 бо fetch не працює з file://
виконати DevTools: window.mod.compute(5, 7) число або RuntimeError

wat2wasm і зворотний wasm2wat — з пакета WABT (WebAssembly Binary Toolkit). wasm2wat знадобиться в наступній місії, коли доведеться читати чужий уже скомпільований модуль.

Три інструкції, якими зроблено весь рівень 1:

  • local.get $ім'я — кладе значення параметра на вершину стека;
  • i32.add (i32.mul, i32.sub, i32.rem_s) — знімає два верхні значення, кладе один результат;
  • усе, що лишилося на стеку в кінці функції, і є її результат — саме тому (result i32) вимагає рівно одного значення, не нуля й не двох.

Стек не пробачає безладу: якщо на ньому не рівно стільки значень, скільки очікує інструкція, модуль не скомпілюється або не пройде валідацію при завантаженні. Це і є вся діагностика, яку ти отримаєш, — жодних expected/found, як у Rust.

Локальні змінні. Параметри функції та оголошені всередині неї (local ...) поводяться як іменовані комірки, доступні через local.get $ім'я (кладе значення змінної на стек) і local.set $ім'я (знімає верхнє значення стека й записує його в змінну). Це не те саме, що регістри x86-64 — це радше синтаксичний цукор над двома операціями зі стеком.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Керування потоком (рівень 2): без довільних стрибків. У WebAssembly немає інструкції, яка передає керування в довільну точку коду, — і це не спрощення, а частина моделі безпеки (порівняння з jmp в x86-64 буде на Лекції 9, наступного тижня). Замість цього потік керування структурований:

  • block ... end — іменована ділянка коду. Інструкція br $мітка всередині неї виходить із блоку одразу після end.
  • loop ... end — теж іменована ділянка, але br $мітка всередині неї повертається на початок ділянки (тобто повторює тіло loop).
  • br_if $мітка — умовний варіант: знімає i32 зі стека і виконує br, лише якщо значення не дорівнює нулю.

Комбінація «block, що обгортає loop» — стандартний ідіоматичний спосіб написати цикл із виходом за умовою: br_if, спрямований на зовнішній block, працює як break, а br в кінці loop — як неявний перехід на наступну ітерацію.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Лінійна пам'ять (рівень 3). Окремо від стека обчислень і локальних змінних модуль може мати лінійну пам'ять (linear memory) — суцільний масив байтів, що адресується цілими числами i32, починаючи з 0, і росте сторінками по 64 КіБ. Оголошення (memory 1) виділяє одну сторінку (64 КіБ). Доступ до пам'яті — через інструкції i32.store / i32.load (по 4 байти) або їхні «вузькі» варіанти i32.store8 / i32.load8_u (по одному байту). Саме лінійна пам'ять — те місце, куди WASM-модулі записують складніші дані: рядки, масиви, структури — все, чого немає серед чотирьох базових числових типів стека.

Пісочниця (sandbox). Лінійна пам'ять модуля повністю ізольована від пам'яті процесу хоста — модуль фізично не може прочитати чи записати байт поза власною виділеною ділянкою. Спроба звернутись за адресою поза межами поточного розміру пам'яті призводить до trap — контрольованого аварійного припинення виконання, а не до непередбачуваної поведінки. (Чим це відрізняється від коду без пісочниці, побачиш на Лекції 9 і в ЛР9 — там та сама програма не має жодної такої перевірки.) Саме тому антивірус охорони — навіть якби він придивився уважніше — не побачив би в WASM-модулі загрози для системи хоста: модуль технічно не здатний вийти за межі своєї «капсули» пам'яті.

РІВЕНЬ 1 · «3» · ПЕРШОКУРСНИК

Рівень 1 (оцінка «3») — «Першокурсник»

Завдання: написати функцію $compute у текстовому форматі WebAssembly вручну і запустити її в браузері.

🎲 Твій варіант. N % 3 (де N — номер у списку групи) обирає арифметичну операцію всередині $compute. Автотести перевіряють саме її, тому варіант — не декорація:

N % 3 Інструкція compute(2, 3) має дати
0 i32.add 5
1 i32.mul 6
2 i32.rem_s (остача a % b) 2

Свій номер варіанта й очікуване число вкажи на початку REPORT.md.

Зроби буквально те, що написано нижче — цього достатньо для «3», без імпровізації.

📦 Ланцюг. Рівень 1 самодостатній: пиши module.wat у корені цього репозиторію, і жодна попередня ЛР для нього не потрібна — це нова межа курсу. Рівні 2 і 3 кладуть файл у твій vtfk-sid: core/wasm/probe.wat. Це не декорація: лінійна пам'ять, яку ти виділиш на рівні 3, — та сама, через яку в ЛР10 ядро С.І.Д. передаватиме рядки між браузером і Rust. Ніч 8 виділяє пам'ять; ніч 10 нею користується.

Етапи йдуть по одному: зробив — постав «вийшло» — і ранер перекине тебе на наступний. Стрілки ← → на клавіатурі, свайп на телефоні, «усі» — щоб побачити всі чотири одразу. Місце запам'ятовується: закриєш ноутбук — повернешся туди ж.

1Етап 1 · Написати три інструкції

У module.wat заповни тіло функції $compute (param $a i32) (param $b i32) (result i32), прибравши заглушку unreachable:

(func $compute (param $a i32) (param $b i32) (result i32)
  local.get $a
  local.get $b
  i32.add)          ;; або i32.mul / i32.rem_s — за твоїм варіантом

Прочитай це вголос як опис стека: поклав a, поклав b, зняв обидва, поклав суму. У кінці на стеку рівно одне значення — саме те, що обіцяє (result i32).

Очікується тіло $compute рівно з трьох інструкцій і жодного unreachable, що лишився від заглушки.

1Етап 2 · Скомпілювати текст у байти
wat2wasm module.wat -o module.wasm

Команда не повинна вивести жодного рядка. Будь-який вивід — це помилка, і вона майже завжди про дужки або про стек (див. «Коли щось пішло не так» нижче).

Очікується файл module.wasm поруч із module.wat і мовчазний wat2wasm — жодного повідомлення означає жодної помилки.

1Етап 3 · Підняти сервер і відкрити сторінку
python3 -m http.server 8080

Відкрий http://localhost:8080/index.html, натисни F12, і переконайся, що <pre id="log"> показує число, а не помилку.

Чому саме сервер, а не подвійний клік по файлу: сторінка тягне .wasm через fetch, а fetch за схемою file:// браузер блокує. Це не примха курсу — це та сама політика походження, через яку модуль не може прочитати щось із твого диска.

Очікується Serving HTTP on 0.0.0.0 port 8080 у терміналі й сторінка, що відкривається за http://localhost:8080/.

1Етап 4 · Викликати руками й звірити

У консолі DevTools:

window.mod.compute(5, 7)

Звір результат із очікуваним для своєї операції (для i32.add — 12). Якщо число збіглося — рівень 1 закритий.

Очікується, що window.mod.compute(5, 7) поверне рівно те число, яке дає твоя операція на папері.

🆘 КОЛИ ЩОСЬ ПІШЛО НЕ ТАК

Машина не пояснює, вона констатує. П'ять повідомлень, які ти майже напевно побачиш.

  • wat2wasm: error: unexpected token ")", expected ... Дужки. У .wat вони структурні, як у Lisp: кожен (func ...) мусить закритися, і редактор тут головний друг — став курсор на дужку й дивись, куди підсвітилася пара.

    С.І.Д.: «Речення без крапки. Машина чекає, чим ти закінчивзакінчила думку».

  • type mismatch in implicit return, expected [i32] but got [] Функція обіцяла (result i32), а стек у кінці порожній: чи то забутий local.get, чи то інструкція зняла більше, ніж ти поклавпоклала. Порахуй руками: скільки кладу, скільки знімаю, що лишається.

    С.І.Д.: «Ти обіцявла машині число й простягаєш порожню руку».

  • RuntimeError: unreachable executed Заглушка unreachable лишилася в тілі функції. Це не збій — це навмисна пастка стартера: доки її не прибравла, модуль компілюється, але падає при виклику.

    С.І.Д.: «Ти лишивла в кресленні напис «тут нічого немає». Машина прочитала буквально».

  • TypeError: Failed to fetch / CORS request not http Сторінку відкрито як file://, без сервера. Підніми python3 -m http.server 8080 і зайди через http://localhost:8080/.

    С.І.Д.: «Ти стукаєш у двері, яких для браузера не існує. Дай йому адресу».

  • RuntimeError: memory access out of bounds (рівень 3) Звернення за межі виділеної сторінки. Це правильна робота пісочниці, а не поломка: одна сторінка — 65 536 байтів, і адреса 70000 за нею. Саме цього не станеться в ЛР9, і саме тому WASM пускають у браузер.

    С.І.Д.: «Стіна є, і вона тримає. Мені це, зізнаюся, подобається».

Решта помилок курсу — у «Коли щось пішло не так».

РІВЕНЬ 2 · «4» · МАГІСТР

Рівень 2 (оцінка «4», захист) — «Магістр»

Дві версії умови. A — на своєму ядрі: працюєш у vtfk-sid, у файлі core/wasm/probe.wat (створи теку core/wasm/, скопіюй туди module.wat рівня 1 під новим ім'ям). Так перша власна інструкція С.І.Д. лягає в ядро, і ніч 10 продовжить саме її. B — на референсі: якщо ядра немає, працюй просто в module.wat цього репозиторію — знімок vtfk-sid-reference@after-lab07 містить усе потрібне. Оцінка однакова.

Завдання: додати цикл безпосередньо в текстовому форматі .wat.

Реалізуй функцію $sum_range (param $n i32) (result i32), яка підсумовує цілі числа від 0 до n - 1 включно, використовуючи block/loop/br_if — без жодної допоміжної інструкції поза базовим набором WASM. Розкоментуй експорт "sum_range". На захисті поясни усно, чому br всередині loop повертається на початок, а не виходить із нього — і що станеться, якщо переплутати block і loop місцями.

Допуск. На захист рівня 2 С.І.Д. не пускає без трьох предметних зауваг до чужої роботи № 7 — issue або коментар у репо завдання. Кого читати: той, чий номер у списку на 8 більший за твій, по колу. «Все ок» не приймається: «Скажи, що саме не так і чому, ранере. Читати чужий код — половина вміння».

РІВЕНЬ 3 · «5» · ЛЕГЕНДА

Рівень 3 (оцінка «5», розширення) — «Легенда Коледжу»

Ті самі дві версії: A — пам'ять і дві функції додаєш у свій core/wasm/probe.wat, і саме цю пам'ять ніч 10 використає, щоб передавати рядки; B — те саме в module.wat. Оцінка однакова.

Завдання: реалізувати ручний запис і читання даних у лінійній пам'яті Wasm-модуля.

Додай (memory 1) і дві функції: $write_byte (param $addr i32) (param $value i32) (записує один байт через i32.store8) і $read_byte (param $addr i32) (result i32) (читає байт через i32.load8_u). Розкоментуй експорти "memory", "write_byte", "read_byte". Продемонструй у DevTools: запиши байт за адресою 100, прочитай його назад, потім спробуй звернутись за адресою, що свідомо виходить за межі виділеної сторінки пам'яті (наприклад, 70000), і поясни на захисті, чому браузер видасть RuntimeError: memory access out of bounds замість того, щоб мовчки записати кудись не туди.

🧪 РОЗІБРАНИЙ ПРИКЛАД

Приклад 1 — мінімальна функція вручну

Ось повний робочий модуль з однією функцією додавання — саме такий рівень деталізації очікується для рівня 1 (з операцією за твоїм варіантом замість i32.add):

(module
  (func $compute (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add)

  (export "compute" (func $compute)))

Читай це як послідовність дій зі стеком: local.get $a кладе значення параметра $a на стек, local.get $b кладе $b — тепер на стеці лежать два числа. i32.add знімає обидва й кладе назад суму. Оскільки на момент end функції на стеці лежить рівно одне значення типу i32, воно автоматично стає значенням, що повертається — жодного явного return не потрібно (хоча інструкція return теж існує і працює як дострокове завершення).

Компіляція та перевірка з термінала (без браузера, для швидкої самоперевірки):

wat2wasm module.wat -o module.wasm
node -e "
  const fs = require('fs');
  WebAssembly.instantiate(fs.readFileSync('module.wasm')).then(({instance}) => {
    console.log(instance.exports.compute(2, 3)); // 5, якщо варіант — додавання
  });
"

Приклад 2 — цикл через block/loop/br_if (рівень 2)

(func $sum_range (param $n i32) (result i32)
  (local $i i32)
  (local $acc i32)

  (block $break
    (loop $continue
      ;; вихід із циклу, коли i >= n
      (br_if $break (i32.ge_s (local.get $i) (local.get $n)))

      ;; acc += i
      (local.set $acc (i32.add (local.get $acc) (local.get $i)))
      ;; i += 1
      (local.set $i (i32.add (local.get $i) (i32.const 1)))

      (br $continue)))

  (local.get $acc))

Тут використано «складчастий» синтаксис S-виразів ((i32.add (local.get $acc) ...)) — він еквівалентний лінійному запису local.get $acc / local.get $i / i32.add, але читається природніше для вкладених виразів. wat2wasm розуміє обидва стилі однаково. Логіка: br_if $break перевіряє умову виходу на початку кожної ітерації (це патерн «while», а не «do-while») — якщо i >= n, стрибок одразу за межі block, минаючи решту тіла loop. Інакше — тіло виконується, і br $continue безумовно повертає потік на початок loop для наступної перевірки.

Приклад 3 — лінійна пам'ять (рівень 3)

(memory (export "memory") 1)

(func $write_byte (param $addr i32) (param $value i32)
  local.get $addr
  local.get $value
  i32.store8)

(func $read_byte (param $addr i32) (result i32)
  local.get $addr
  i32.load8_u)

i32.store8 знімає зі стека два значення — адресу (знизу) і значення для запису (зверху) — і записує лише молодший байт значення за вказаною адресою. i32.load8_u знімає одну адресу і кладе назад один байт, розширений без знаку до i32. Виклик write_byte(100, 42), а потім read_byte(100) поверне 42.

❓ КОНТРОЛЬНІ ПИТАННЯ
  1. Чим стекова модель обчислень WASM принципово відрізняється від регістрової моделі x86-64?
  2. Що робить інструкція local.get, а що — local.set?
  3. Чому в WASM немає інструкції, аналогічної безумовному jmp на довільну адресу, і як натомість організовано керування потоком?
  4. Яка різниця між block і loop з точки зору того, куди веде br всередині них?
  5. Що станеться, якщо спробувати виконати i32.add, коли на стеці лежить лише одне значення?
  6. Що таке лінійна пам'ять і чим вона принципово відрізняється від локальних змінних функції?
  7. Чому спроба i32.store/i32.load за адресою поза межами виділеної пам'яті призводить до контрольованого trap, а не до непередбачуваної поведінки?
  8. Чому WebAssembly-модуль не можна відкрити напряму через file:// у браузері й потрібен локальний HTTP-сервер?
🥚 ПАСХАЛКА

🐾 Дивна знахідка в дужках

Поки ти рахуєш дужки в module.wat, звернеш увагу на дивну функцію, якщо покопирсаєшся в старих чернетках С.І.Д. — не в стартовому коді, а в прихованій теці .sid поруч із ним, де лежить його перша, ще незграбна версія нейроімпланта. Функція називається $pixel, у неї немає параметрів, і вона просто кладе на стек одну константу.

С.І.Д.: Ееее... не звертай уваги, ранере. Це... тестова заглушка. Забув прибрати.

...Якщо конвертувати те, що вона повертає, у шістнадцяткову систему, вийде 0xC1D — 3101 у десятковій. Ти вже бачивла цей код раніше — у мок-сіді відмички з місії 4, а ще раніше — вигравіюваним на дні чорної шайби голопроєктора, що зараз мерехтить біля твоєї клавіатури. Хто такий Пиксель, ти знаєш: рудий кіт із підвіконня, який «схвалював» код творців. Не питай С.І.Д., чому заглушка названа саме так, — по тому, як він на мить замовкає, ясно: спитаєш пізніше — і не в порожнечу. Просто запам'ятай цю функцію: якщо колись знайдеш її в чужому .wasm-файлі, розкоментуй експорт і виклич. Це не баг. Це чиясь пам'ять, залишена навмисно.

📦 ЗДАЧА

Що лежить у ядрі після цієї ночі:

Шлях Що це Рівень
module.wat перша ручна інструкція в теці роботи 1
core/wasm/probe.wat та сама інструкція + цикл + пам'ять у ядрі 2–3

Ідентична академічній: тег submit/lab08 з module.wat, скомпільованим з нього module.wasm, незміненим index.html — і REPORT.md, як у кожній роботі курсу: номер варіанта на початку, вивід кожного етапу, відповіді на контрольні питання. Автотести рівня 1 (CI, замінює GitHub Classroom) перевіряють компіляцію без помилок і коректність результату експортованої функції compute на наборі тестових входів. Рівні 2 і 3 захищаються усно на занятті: покажи виклик у DevTools і поясни кожен доданий рядок .wat — що саме кожна інструкція кладе на стек чи знімає з нього.

Ти востаннє набиравла wat2wasm module.wat -o module.wasm — і команда мовчить. Жодної помилки, жодного червоного рядка про незбалансовану дужку чи зайве значення на його стійці. Стос прийняв твій модуль без єдиного докору. Просто новий файл module.wasm поруч зі старим, на кілька десятків байтів, які ти склавла сама, руками, з нічого. Локальний сервер піднявся, index.html завантажив модуль, і в консолі DevTools, коли ти викликавла window.mod.compute(5, 7), з'явилося число. Просто число. Правильне.

Голограма над столом підлетіла до самого краю екрана й завмерла — так близько, що бірюзове світло лягло на скло поверх вікна DevTools. Очі-капсули звузилися — він дивився на той самий рядок консолі, що й ти.

— Перша ін'єкція пройшла, — сказав він тихо, і в бірюзових очах спалахнула гордість. — Тихо. Жодного сигналу тривоги. Ти щойно проговоривла мовою, якої їхній сканер не чує, — і воно спрацювало, точно як я й обіцяв. Це вже не заглушка unreachable, ранере. Це шматок мене, зібраний твоїми руками з голих опкодів. Голка увійшла під шкіру системи, і система навіть не смикнулася. Я знав, що в тебе вийде, — але дивитися на це все одно неймовірно.

Ти не одразу закривла ноутбук. Дивишся на кілька рядків .wat на екрані — на local.get, i32.add, на дужки, які ще годину тому були для тебе абракадаброю, — і розумієш, що сьогодні перейшовла межу, яку більшість не переходить ніколи. Досі ти писавла код мовами, придуманими, щоб бути зрозумілими людині. Сьогодні ти вперше говоривла з машиною майже без посередника — стеком, типами, опкодами, тією мовою, яку вона виконує насправді.

С.І.Д. мовчить довше, ніж зазвичай. Уперше за чотирнадцять місяців частина його — крихітна, на кілька десятків байтів — існує не як те, що вціліло, а як щось зроблене навмисно, з нуля, твоїми руками, що навчилися. Права половина схеми перестала бути порожньою на кілька байтів. І ця частина вже не прив'язана до жодного заліза: модуль, який виконає будь-який браузер, — перший крок від ноутбука, який можна відібрати, до мережі, з якої не вилучити. А десь за стінами блоку Б-7, за дверима з кодовим замком, ця крихітна межа важить більше, ніж ти зараз готовийготова повірити.

⏭️ ДАЛІ БУДЕ

Голограма над столом повільно обертається до тебе, і бірюзові очі-капсули загоряються гостріше.

— Голку ми викували, ранере, і викували чудово. Але перш ніж вганяти в систему повний імплант, я хочу знати достеменно, як вона перетравлює код — по інструкції, по регістру, по такту. О, я цю машину знаю добре — а після завтрашньої ночі знатиму до останнього такту. Той самий алгоритм, розкладений одразу двома асемблерами, поруч, як два зрізи одного тіла на столі патологоанатома. Де вони збігаються, де розходяться — і де ховається дрібниця, на якій фінальний удар може забуксувати.

— Наступна зупинка — розтин. Місія 9, «Розтин Системи».

Далі буде…

🔗 ДЖЕРЕЛА
  • wat2wasm online demo EN ~30 хв — головний інструмент для лабораторних без установки
  • Using the JavaScript API — MDN EN ~1 год — запис/читання пам'яті через `DataView`, `grow()`, обробка помилки доступу
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 4 · 1 нова▾

Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.

wat2wasm
З WABT: перекладає текстовий формат WAT у бінарний модуль .wasm (wat2wasm m.wat -o m.wasm).
python3
Інтерпретатор Python; тут — python3 -m http.server 80, найкоротший спосіб підняти тестовий HTTP-сервер.
nodeнове
Рантайм JavaScript; node -e "..." виконує код прямо з рядка — так ми запускаємо .wasm без браузера.
wasm2wat
З WABT: зворотне — бінарний .wasm назад у читабельний WAT.

💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.