QEMU.js: elo sérieux a mat WASM

Eemol op enger Zäit decidéiert ech fir Spaass beweisen d'Reversibilitéit vum Prozess a léiert wéi Dir JavaScript (méi präzis, Asm.js) aus Maschinncode generéiert. QEMU gouf fir den Experiment gewielt, an eng Zäit méi spéit gouf en Artikel iwwer Habr geschriwwen. An de Kommentarer gouf ech ugeroden de Projet an der WebAssembly nei ze maachen, a souguer selwer opzehalen bal fäerdeg Ech wollt iergendwéi net de Projet ... D'Aarbecht war weidergaang, awer ganz lues, an elo, viru kuerzem an deem Artikel erschéngt commentaire zum Thema "Also wéi ass et alles eriwwer?" Als Äntwert op meng detailléiert Äntwert hunn ech héieren "Dëst kléngt wéi en Artikel." Gutt, wann Dir kënnt, gëtt et en Artikel. Vläicht fannt een et nëtzlech. Doraus léiert de Lieser e puer Fakten iwwer den Design vu QEMU Code Generatioun Backends, wéi och wéi een e Just-in-Time Compiler fir eng Webapplikatioun schreift.

Aufgaben

Well ech scho geléiert hunn wéi een QEMU op JavaScript "irgendwéi" portéiert, dës Kéier gouf decidéiert et clever ze maachen an net al Feeler ze widderhuelen.

Feeler Nummer eent: Branche vum Punkt Verëffentlechung

Mäin éischte Feeler war meng Versioun vun der Upstream Versioun 2.4.1 ze gabel. Dunn huet et mir eng gutt Iddi geschéngt: wann Punkt Verëffentlechung existéiert, dann ass et wahrscheinlech méi stabil wéi einfach 2.4, an nach méi sou d'Branche master. A well ech geplangt eng fair Quantitéit vu menge eegene Käfere derbäi ze setzen, hunn ech iwwerhaapt keen aneren gebraucht. Dat ass wuel wéi et erausgaang ass. Awer hei ass d'Saach: QEMU steet net roueg, an iergendwann hunn se souguer d'Optimiséierung vum generéierte Code mat 10 Prozent ugekënnegt. "Jo, elo wäert ech afréieren", hunn ech geduecht a gebrach. Hei musse mir eng Digression maachen: wéinst der Single-threaded Natur vu QEMU.js an der Tatsaach, datt d'Original QEMU net d'Feele vu Multi-threading implizéiert (dat ass, d'Fäegkeet fir gläichzäiteg verschidde onrelatéiert Codeweeër ze bedreiwen, an net nëmmen "benotzen all Kären") ass kritesch dofir, d'Haaptfunktioune vun Threads hunn ech missen "ausdréien" fir vu baussen ze ruffen. Dëst huet e puer natierlech Problemer während der Fusioun. Allerdéngs ass d'Tatsaach, datt e puer vun den Ännerungen aus der Branche master, Mat deem probéiert ech mäi Code ze fusionéieren, goufen och Kiischt an der Punkt Verëffentlechung (an dofir a mengem Branche) och wahrscheinlech net dobäi Kamoudheet.

Am Allgemengen hunn ech décidéiert datt et nach ëmmer Sënn mécht de Prototyp eraus ze werfen, et fir Deeler ze disassemble an eng nei Versioun vun Null ze bauen baséiert op eppes méi frësch an elo vun master.

Feeler Nummer zwee: TLP Methodik

Am Wesentlechen ass dëst kee Feeler, am Allgemengen ass et just eng Feature fir e Projet ze kreéieren ënner Bedingunge vu komplette Mëssverständnis vu "wou a wéi ze plënneren?" an am Allgemengen "kréie mir do?" An dëse Konditiounen knaschteg programméiere war eng gerechtfäerdegt Optioun, awer, natierlech, ech wollt et net onnéideg widderhuelen. Dës Kéier wollt ech et clever maachen: atomar Verpflichtungen, bewosst Code Ännerungen (an net "Zoufälleg Zeeche matenee zéien bis et zesummegesat gëtt (mat Warnungen)", wéi de Linus Torvalds eemol iwwer een gesot huet, laut Wikiquote), etc.

Feeler Nummer dräi: an d'Waasser kommen ouni de Ford ze kennen

Ech sinn nach net ganz vun dëser lass, mee elo hunn ech decidéiert de Wee vun der mannsten Resistenz iwwerhaapt net ze verfollegen, an et "als Erwuessenen" ze maachen, nämlech meng TCG Backend vun Null ze schreiwen, fir net méi spéit musse soen: "Jo, dëst ass natierlech lues, awer ech kann net alles kontrolléieren - dat ass wéi TCI geschriwwe gëtt ..." Desweideren huet dëst am Ufank wéi eng offensichtlech Léisung geschéngt, zënter Ech generéieren binäre Code. Wéi se soen, "Gent versammeltу, awer net deen": de Code ass natierlech binär, awer d'Kontroll kann net einfach drop iwwerdroen ginn - et muss explizit an de Browser gedréckt ginn fir ze kompiléieren, wat zu engem bestëmmten Objet aus der JS Welt resultéiert, wat nach ëmmer muss iergendwou gerett ginn. Wéi och ëmmer, op normalen RISC Architekturen, souwäit ech verstinn, ass eng typesch Situatioun de Besoin fir den Instruktiounscache fir regeneréierte Code explizit zréckzesetzen - wann dat net ass wat mir brauchen, dann ass et op alle Fall no. Zousätzlech, vu mengem leschte Versuch, hunn ech geléiert datt d'Kontroll net an d'Mëtt vum Iwwersetzungsblock iwwerdroe schéngt, also brauche mir net wierklech Bytecode interpretéiert vun all Offset, a mir kënnen et einfach aus der Funktioun op TB generéieren .

Si koumen an hu geschloen

Och wann ech ugefaang hunn de Code zréck am Juli nei ze schreiwen, ass e magesche Kick onopfälleg gekräizt: normalerweis Bréiwer vum GitHub kommen als Notifikatiounen iwwer Äntwerten op Themen a Pull Ufroen, awer hei, op eemol am thread ernimmen Binaryen als qemu Backend am Kontext, "Hien huet esou eppes gemaach, vläicht wäert hien eppes soen." Mir hu geschwat iwwer d'Benotzung vun Emscripten's Zesummenhang Bibliothéik Binär WASM JIT ze kreéieren. Ee, sot ech, datt Dir eng Apache 2.0 Lizenz do hunn, an QEMU als Ganzt ënner GPLv2 verdeelt, a si sinn net ganz kompatibel. Op eemol huet sech erausgestallt, datt eng Lizenz kann befestegt et iergendwéi (Ech weess et net: vläicht änneren, vläicht duebel Lizenz, vläicht soss eppes ...). Dëst huet mech natierlech frou gemaach, well ech deemools scho gutt gekuckt hunn binäre Format WebAssembly, an ech war iergendwéi traureg an onverständlech. Et gouf och eng Bibliothéik déi d'Basisblocker mat der Iwwergangsgrafik verschlësselt, de Bytecode produzéieren, a souguer am Dolmetscher selwer lafen, wann néideg.

Da gouf et méi e Bréif op der QEMU Mailing Lëscht, awer dëst ass méi iwwer d'Fro: "Wien brauch et iwwerhaapt?" An et ass op eemol, et huet sech erausgestallt datt et néideg war. Op e Minimum kënnt Dir déi folgend Benotzungsméiglechkeeten zesummeschrauwen, wann et méi oder manner séier funktionnéiert:

  • eppes Educatiouns lancéieren ouni iwwerhaapt Installatioun
  • Virtualiséierung op iOS, wou, laut Rumeuren, déi eenzeg Applikatioun déi d'Recht huet fir Code Generatioun op der Flucht ass e JS Motor (ass dat wouer?)
  • Demonstratioun vu Mini-OS - Single-Floppy, Built-in, all Zorte vu Firmware, etc ...

Browser Runtime Features

Wéi ech scho gesot hunn, ass QEMU u Multithreading gebonnen, awer de Browser huet et net. Gutt, dat ass, nee ... Am Ufank huet et guer net existéiert, dunn sinn WebWorkers opgetaucht - souwäit ech verstinn, ass dëst Multithreading baséiert op Message Passage ouni gemeinsam Verännerlechen. Natierlech schaaft dëst bedeitend Probleemer wann Dir existente Code portéiert op Basis vum gemeinsame Gedächtnismodell. Dunn, ënner ëffentlechen Drock, gouf et och ënnert dem Numm ëmgesat SharedArrayBuffers. Et gouf lues a lues agefouert, si hunn hir Start a verschiddene Browser gefeiert, duerno hunn se d'Neit Joer gefeiert, an dann Meltdown ... Duerno koumen se zur Konklusioun datt d'Zäitmiessung gro oder grober ass, awer mat Hëllef vu gemeinsame Gedächtnis an engem Fuedem de Konter inkrementéiert, et ass alles d'selwecht et klappt zimlech genee aus. Also hu mir Multithreading mat gedeelt Erënnerung behënnert. Et schéngt, datt se et spéider erëm opgemaach hunn, awer, wéi et aus dem éischten Experiment kloer gouf, gëtt et Liewen ouni et, a wa jo, probéieren mir et ze maachen ouni op Multithreading ze vertrauen.

Déi zweet Feature ass d'Onméiglechkeet vu Low-Level Manipulatioune mam Stack: Dir kënnt net einfach huelen, den aktuelle Kontext späicheren an op en neien mat engem neie Stack wiesselen. Den Uruffstack gëtt vun der JS virtueller Maschinn geréiert. Et schéngt, wat ass de Problem, well mir nach ëmmer décidéiert hunn déi fréier Flows komplett manuell ze managen? D'Tatsaach ass datt Block I / O an QEMU duerch Coroutines ëmgesat gëtt, an dat ass wou Low-Level Stack Manipulatioune praktesch kommen. Glécklecherweis enthält Emscipten schonn e Mechanismus fir asynchron Operatiounen, och zwee: Asyncify и Emterpreter. Déi éischt funktionnéiert duerch bedeitend Bloat am generéierten JavaScript Code an gëtt net méi ënnerstëtzt. Déi zweet ass den aktuellen "korrekte Wee" a funktionnéiert duerch Bytecode Generatioun fir den gebiertege Dolmetscher. Et funktionnéiert, natierlech, lues, awer et bloat de Code net. True, Ënnerstëtzung fir Coroutines fir dëse Mechanismus muss onofhängeg bäigedroen ginn (et goufe scho Coroutine geschriwwe fir Asyncify an et gouf eng Ëmsetzung vun ongeféier déiselwecht API fir Emterpreter, Dir musst se just verbannen).

Am Moment hunn ech et nach net fäerdeg bruecht de Code opzedeelen an een am WASM kompiléiert an mat Emterpreter interpretéiert, sou datt d'Blockgeräter nach net funktionnéieren (kuckt an der nächster Serie, wéi se soen ...). Dat ass, um Enn sollt Dir eppes wéi dës witzeg Layer Saach kréien:

  • interpretéiert Block ech / O. Gutt, hutt Dir wierklech emuléiert NVMe mat gebierteg Leeschtung erwaart? 🙂
  • statesch kompiléiert Haapt QEMU Code (Iwwersetzer, aner emuléiert Geräter, etc.)
  • dynamesch kompiléiert Gaaschtcode an WASM

Fonctiounen vun QEMU Quellen

Wéi Dir wahrscheinlech scho virgestallt hutt, sinn de Code fir d'Emuléierung vun Gaaschtarchitekturen an de Code fir Hostmaschinninstruktiounen ze generéieren an QEMU getrennt. Tatsächlech ass et souguer e bësse méi schwiereg:

  • et gi Gaaschtarchitekturen
  • ass Beschleuniger, nämlech KVM fir Hardwarevirtualiséierung op Linux (fir Gaascht- an Hostsystemer kompatibel mateneen), TCG fir JIT Code Generatioun iwwerall. Start mat QEMU 2.9, Ënnerstëtzung fir den HAXM Hardware Virtualiséierungsstandard op Windows erschéngt (d'Detailer)
  • wann TCG benotzt gëtt an net Hardwarevirtualiséierung, dann huet et separat Code Generatioun Ënnerstëtzung fir all Hostarchitektur, souwéi fir den universellen Dolmetscher
  • ... a ronderëm all dëst - emuléiert Peripheriegeräter, User Interface, Migratioun, Record-Replay, etc.

Wousst Dir iwwregens: QEMU kann net nëmmen de ganze Computer emuléieren, awer och de Prozessor fir e separaten Benotzerprozess am Hostkär, deen zum Beispill vum AFL Fuzzer fir binär Instrumentatioun benotzt gëtt. Vläicht géif een dësen Operatiounsmodus vu QEMU op JS portéieren? 😉

Wéi déi meescht laangjäreg gratis Software, ass QEMU duerch den Uruff gebaut configure и make. Loosst eis soen datt Dir décidéiert eppes ze addéieren: en TCG Backend, Thread Implementatioun, soss eppes. Maacht Iech net séier glécklech / erschreckt ze sinn (ënnersträichen wéi passend) beim Perspektiv fir mat Autoconf ze kommunizéieren - tatsächlech, configure QEMU's ass anscheinend selwer geschriwwen a gëtt aus näischt generéiert.

WebAssemblée

Also wat ass dës Saach genannt WebAssembly (alias WASM)? Dëst ass en Ersatz fir Asm.js, mécht net méi wéi e gültege JavaScript Code ze sinn. Am Géigendeel, et ass reng binär an optimiséiert, an och einfach eng ganz Zuel an et ze schreiwen ass net ganz einfach: fir Kompaktheet ass et am Format gespäichert LEB128 Fotoen.

Dir hutt vläicht iwwer de Relooping Algorithmus fir Asm.js héieren - dëst ass d'Restauratioun vun "Héichniveau" Flow Kontrollinstruktiounen (dat ass, wann-dan-soen, Schleifen, etc.), fir déi JS Motore entworf sinn, vun den nidderegen Niveau LLVM IR, méi no beim Maschinncode, deen vum Prozessor ausgefouert gëtt. Natierlech ass d'Zwëschenvertriedung vu QEMU méi no un der zweeter. Et géif schéngen, datt hei ass, Bytecode, d'Enn vun der Péng ... An da ginn et Spären, wann-dan-soen a Schleifen! ..

An dëst ass en anere Grond firwat Binaryen nëtzlech ass: et kann natierlech High-Level-Blöcke akzeptéieren no bei deem wat am WASM gelagert gëtt. Awer et kann och Code produzéieren aus enger Grafik vu Basisblocken an Iwwergäng tëscht hinnen. Gutt, ech hu scho gesot datt et de WebAssembly Späicherformat hannert der praktescher C / C ++ API verstoppt.

TCG (Tiny Code Generator)

GTC war ursprénglech Backend fir den C Compiler.Da, anscheinend, konnt et d'Konkurrenz mam GCC net widderstoen, awer um Enn huet et seng Plaz am QEMU als Code Generatiounsmechanismus fir d'Hostplattform fonnt. Et gëtt och en TCG-Backend deen e puer abstrakte Bytecode generéiert, deen direkt vum Dolmetscher ausgefouert gëtt, awer ech hunn decidéiert dës Kéier ze vermeiden. Wéi och ëmmer, d'Tatsaach datt am QEMU et scho méiglech ass den Iwwergank zum generéierten TB duerch d'Funktioun z'erméiglechen tcg_qemu_tb_exec, et huet sech als ganz nëtzlech fir mech erausgestallt.

Fir en neien TCG Backend op QEMU ze addéieren, musst Dir e Subdirectory erstellen tcg/<имя архитектуры> (an dësem Fall, tcg/binaryen), an et enthält zwee Dateien: tcg-target.h и tcg-target.inc.c и virschreiwen et ass alles ëm configure. Dir kënnt aner Dateien do setzen, awer, wéi Dir aus den Nimm vun dësen zwee kënnt roden, ginn se allebéid iergendwou abegraff: een als normal Header-Datei (et ass abegraff an tcg/tcg.h, an deen ass schonn an anere Fichieren an de Verzeichnisser tcg, accel an net nëmmen), déi aner - nëmmen als Code Snippet an tcg/tcg.c, awer et huet Zougang zu senge statesche Funktiounen.

Entscheeden, datt ech ze vill Zäit op detailléiert Ënnersich verbréngen wéi et Wierker, Ech kopéiert einfach de "Skelett" vun dësen zwee Fichier'en aus engem anere Backend Ëmsetzung, éierlech uginn dat an der Lizenz Header.

Fichier tcg-target.h enthält haaptsächlech Astellungen an der Form #define-s:

  • wéi vill Registere a wéi eng Breet sinn et op der Zilarchitektur (mir hunn esou vill wéi mir wëllen, sou vill wéi mir wëllen - d'Fro ass méi iwwer wat méi effizient Code vum Browser op der "komplett Zil" Architektur generéiert gëtt ...)
  • Ausrichtung vun Hostinstruktiounen: op x86, an och am TCI, sinn d'Instruktioune guer net ausgeriicht, awer ech wäert an de Codebuffer guer keng Instruktioune setzen, awer Hiweiser op Binaryen Bibliothéikstrukturen, also wäert ech soen: 4 bytes
  • wéi eng fakultativ Instruktiounen de Backend generéiere kann - mir enthalen alles wat mir am Binaryen fannen, loosst de Beschleuniger de Rescht an méi einfach selwer briechen
  • Wat ass déi geschätzte Gréisst vum TLB Cache gefrot vum Backend. D'Tatsaach ass, datt am QEMU alles eescht ass: obwuel et Hëllefsfunktiounen sinn, déi d'Laascht / Store berécksiichtegen andeems de Gaascht MMU berécksiichtegt (wou wiere mir elo ouni et?), späicheren se hiren Iwwersetzungscache a Form vun enger Struktur, de Veraarbechtung vun deem bequem ass direkt an d'Sendungsblocken z'integréieren. D'Fro ass wéi eng Offset an dëser Struktur am effizientesten duerch eng kleng a séier Sequenz vu Kommandoen veraarbecht gëtt?
  • hei kënnt Dir den Zweck vun engem oder zwee reservéierte Registere tweaken, aktivéieren TB duerch eng Funktioun ze ruffen an optional e puer kleng beschreiwen inline- Funktiounen wéi flush_icache_range (awer dëst ass net eise Fall)

Fichier tcg-target.inc.c, natierlech, ass normalerweis vill méi grouss an der Gréisst an enthält verschidde obligatoresch Funktiounen:

  • Initialiséierung, inklusiv Restriktiounen op wéi eng Instruktioune kënne funktionnéieren op wéi eng Operander. Blatant vun mir vun engem aneren Backend kopéiert
  • Funktioun déi eng intern Bytecode Instruktioun hëlt
  • Dir kënnt och Hëllef Fonctiounen hei, an Dir kënnt och statesch Funktiounen benotzen aus tcg/tcg.c

Fir mech hunn ech déi folgend Strategie gewielt: an den éischte Wierder vum nächsten Iwwersetzungsblock hunn ech véier Indikatiounen opgeschriwwen: e Startmark (e bestëmmte Wäert an der Géigend) 0xFFFFFFFF, deen den aktuellen Zoustand vun der TB bestëmmt huet), Kontext, generéiert Modul a Magienummer fir Debugging. Am Ufank gouf d'Mark gesat 0xFFFFFFFF - nwou n - eng kleng positiv Zuel, an all Kéier wann se duerch den Dolmetscher ausgeführt gouf, huet se ëm 1. Wann et erreecht 0xFFFFFFFE, d'Kompilatioun stattfonnt huet, de Modul gouf an der Funktiounstabell gespäichert, an e klenge "Launcher" importéiert, an deem d'Ausféierung vun tcg_qemu_tb_exec, an de Modul war aus QEMU Erënnerung geläscht.

Fir d'Klassiker ze paraphraséieren, "Crutch, wéi vill ass an dësem Toun fir d'Häerz vum Proger verwéckelt ...". Allerdéngs war d'Erënnerung iergendwou ausgelaf. Ausserdeem war et Erënnerung geréiert vum QEMU! Ech hat e Code, deen beim Schreiwen vun der nächster Instruktioun (gutt, also e Pointer) deen geläscht huet deem säi Link virdru op dëser Plaz war, awer dëst huet net gehollef. Eigentlech, am einfachsten Fall, verdeelt QEMU Erënnerung beim Start a schreift de generéierte Code do. Wann de Puffer leeft, gëtt de Code erausgehäit an deen nächste fänkt un a senger Plaz ze schreiwen.

Nodeems ech de Code studéiert hunn, hunn ech gemierkt datt den Trick mat der magescher Nummer et erlaabt mech net op Koup Zerstéierung ze versoen andeems ech eppes falsch op engem oninitialiséierte Puffer am éischte Pass befreit hunn. Awer wien schreift de Puffer ëm fir meng Funktioun spéider ëmzegoen? Wéi d'Emscripten Entwéckler beroden, wann ech an e Problem gelaf sinn, hunn ech de resultéierende Code zréck an d'native Applikatioun portéiert, Mozilla Record-Replay drop gesat ... Am Allgemengen, am Endeffekt hunn ech eng einfach Saach realiséiert: fir all Block, a struct TranslationBlock mat senger Beschreiwung. Guess wou ... Dat ass richteg, just virum Block direkt am Puffer. Ech hunn dëst gemierkt, ech hu beschloss opzehalen Krytzen ze benotzen (op d'mannst e puer), an hunn einfach déi magesch Nummer erausgehäit an déi verbleiwen Wierder iwwerginn struct TranslationBlock, eng eenzeg verlinkt Lëscht erstellen, déi séier duerchgefouert ka ginn wann den Iwwersetzungscache zréckgesat gëtt, an d'Erënnerung befreit.

Puer Crutches bleiwen: zum Beispill, markéiert Zeeche am Code Prellbock - e puer vun hinnen sinn einfach BinaryenExpressionRef, dat heescht, si kucken op d'Ausdréck déi linear an de generéierte Basisblock gesat musse ginn, Deel ass d'Konditioun fir den Iwwergang tëscht BBs, Deel ass wou ze goen. Gutt, et gi scho virbereet Blocke fir Relooper déi no de Konditioune verbonne musse ginn. Fir se z'ënnerscheeden, gëtt d'Annahme benotzt datt se all op d'mannst véier Bytes ausgeriicht sinn, sou datt Dir sécher déi mannst bedeitend zwee Bits fir de Label benotze kënnt, Dir musst just drun erënneren et ze läschen wann néideg. Iwwregens, esou Etiketten ginn schonn am QEMU benotzt fir de Grond fir d'TCG Loop ze verloossen.

Benotzt Binaryen

Moduler an WebAssembly enthalen Funktiounen, jidderee vun deenen e Kierper enthält, wat en Ausdrock ass. Ausdréck sinn unär a binär Operatiounen, Block besteet aus Lëschte vun aneren Ausdréck, Kontrollfloss, asw. Wéi ech scho gesot hunn, ass d'Kontrollfloss hei genee organiséiert wéi Héichniveau Filialen, Loops, Funktiounsruffen, etc. Argumenter fir Funktiounen ginn net op de Stack weiderginn, awer explizit, grad wéi am JS. Et ginn och global Verännerlechen, mee ech hunn se net benotzt, also ech wäert Iech net iwwer hinnen soen.

Fonctiounen hunn och lokal Verännerlechen, nummeréiert vun null, vun Typ: int32 / int64 / float / duebel. An dësem Fall sinn déi éischt n lokal Variabelen d'Argumenter déi un d'Funktioun weiderginn. Notéiert w.e.g. datt och wann alles hei net ganz niddereg ass wat de Kontrollfluss ugeet, ganz Zuelen nach ëmmer net den Attribut "ënnerschriwwen / net ënnerschriwwen" droen: wéi d'Zuel sech behält hänkt vum Operatiounscode of.

Am Allgemengen, bitt Binaryen einfach C-API: Dir erstellt e Modul, an him Erstellt Ausdréck - unär, binär, Blöcke vun aneren Ausdréck, Kontrollfloss, etc. Da kreéiert Dir eng Funktioun mat engem Ausdrock als Kierper. Wann Dir, wéi ech, e Low-Level Iwwergangsgrafik hutt, hëlleft de Relooper-Komponent Iech. Sou wäit wéi ech verstinn, ass et méiglech héich-Niveau Kontroll vun der Ausféierung Flux an engem Block ze benotzen, soulaang et net iwwer d'Grenze vum Block geet - dat ass, et ass méiglech intern séier Wee / lues ze maachen Wee Verzweigung am agebauten TLB Cache Veraarbechtungscode, awer net fir den "externen" Kontrollfloss ze stéieren. Wann Dir e Relooper befreit, gi seng Blocke befreit; wann Dir e Modul befreit, verschwannen d'Ausdréck, Funktiounen, asw. arena.

Wéi och ëmmer, wann Dir Code op der Flucht interpretéiere wëllt ouni onnéideg Erstelle an Läschen vun enger Dolmetscherinstanz, kann et Sënn maachen dës Logik an eng C++ Datei ze setzen, a vun do direkt de ganze C++ API vun der Bibliothéik ze managen, ëmgoen prett- gemaach wrappers.

Also fir de Code ze generéieren deen Dir braucht

// настроить глобальные параметры (можно поменять потом)
BinaryenSetAPITracing(0);

BinaryenSetOptimizeLevel(3);
BinaryenSetShrinkLevel(2);

// создать модуль
BinaryenModuleRef MODULE = BinaryenModuleCreate();

// описать типы функций (как создаваемых, так и вызываемых)
helper_type  BinaryenAddFunctionType(MODULE, "helper-func", BinaryenTypeInt32(), int32_helper_args, ARRAY_SIZE(int32_helper_args));
// (int23_helper_args приоб^Wсоздаются отдельно)

// сконструировать супер-мега выражение
// ... ну тут уж вы как-нибудь сами :)

// потом создать функцию
BinaryenAddFunction(MODULE, "tb_fun", tb_func_type, func_locals, FUNC_LOCALS_COUNT, expr);
BinaryenAddFunctionExport(MODULE, "tb_fun", "tb_fun");
...
BinaryenSetMemory(MODULE, (1 << 15) - 1, -1, NULL, NULL, NULL, NULL, NULL, 0, 0);
BinaryenAddMemoryImport(MODULE, NULL, "env", "memory", 0);
BinaryenAddTableImport(MODULE, NULL, "env", "tb_funcs");

// запросить валидацию и оптимизацию при желании
assert (BinaryenModuleValidate(MODULE));
BinaryenModuleOptimize(MODULE);

... wann ech eppes vergiess hunn, sorry, dëst ass just fir d'Skala ze representéieren, an d'Detailer sinn an der Dokumentatioun.

An elo fänkt de Crack-Fex-Pex un, sou eppes:

static char buf[1 << 20];
BinaryenModuleOptimize(MODULE);
BinaryenSetMemory(MODULE, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0);
int sz = BinaryenModuleWrite(MODULE, buf, sizeof(buf));
BinaryenModuleDispose(MODULE);
EM_ASM({
  var module = new WebAssembly.Module(new Uint8Array(wasmMemory.buffer, $0, $1));
  var fptr = $2;
  var instance = new WebAssembly.Instance(module, {
      'env': {
          'memory': wasmMemory,
          // ...
      }
  );
  // и вот уже у вас есть instance!
}, buf, sz);

Fir iergendwéi d'Welte vu QEMU a JS ze verbannen a gläichzäiteg séier op d'kompiléiert Funktiounen ze kommen, gouf eng Array erstallt (en Tabelle vu Funktiounen fir den Import an de Launcher), an déi generéiert Funktiounen goufen do plazéiert. Fir den Index séier ze berechnen, gouf den Index vum Null-Wuert Iwwersetzungsblock am Ufank als et benotzt, awer dunn huet den Index, deen mat dëser Formel berechent gëtt, ugefaang einfach an d'Feld ze passen. struct TranslationBlock.

Iwwrégens, Demo (aktuell mat enger düsterer Lizenz) funktionnéiert nëmme gutt am Firefox. Chrome Entwéckler waren iergendwéi net prett zu der Tatsaach, datt iergendeen méi wéi dausend Instanzen vu WebAssembly Moduler wëllt erstellen, sou datt se einfach e Gigabyte vu virtuellen Adressraum fir all ...

Dat ass alles fir de Moment. Vläicht gëtt et nach en Artikel wann een interesséiert ass. Et bleift nämlech op d'mannst nëmmen maachen Block Apparater Aarbecht. Et kéint och Sënn maachen, d'Zesummesetzung vun de WebAssembly Moduler asynchron ze maachen, wéi et an der JS Welt üblech ass, well et nach ëmmer en Dolmetscher gëtt, deen dat alles ka maachen, bis den native Modul fäerdeg ass.

Endlech e Rätsel: Dir hutt e Binär op enger 32-Bit Architektur kompiléiert, awer de Code, duerch Erënnerungsoperatiounen, klëmmt vu Binaryen, iergendwou um Stack oder soss anzwousch an der ieweschter 2 GB vum 32-Bit Adressraum. De Problem ass datt aus Binaryen Siicht dëst Zougang zu enger ze grouss resultéierend Adress ass. Wéi ronderëm dëst ze kréien?

Op Administrateur Manéier

Ech hunn dëst net getest, awer meng éischt Gedanken war "Wat wann ech 32-Bit Linux installéiert hunn?" Da gëtt den ieweschten Deel vum Adressraum vum Kernel besat. Déi eenzeg Fro ass wéi vill wäert besat ginn: 1 oder 2 Gb.

Op engem Programméierer Wee (Optioun fir Praktiker)

Loosst eis eng Bubble uewen am Adressraum blosen. Ech selwer verstinn net firwat et funktionnéiert - do schonn et muss e Stack ginn. Awer "mir sinn Praktiker: alles funktionnéiert fir eis, awer kee weess firwat ..."

// 2gbubble.c
// Usage: LD_PRELOAD=2gbubble.so <program>

#include <sys/mman.h>
#include <assert.h>

void __attribute__((constructor)) constr(void)
{
  assert(MAP_FAILED != mmap(1u >> 31, (1u >> 31) - (1u >> 20), PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
}

... et ass richteg datt et net mat Valgrind kompatibel ass, awer glécklecherweis dréckt d'Valgrind selwer ganz effektiv jiddereen dovun aus :)

Vläicht gëtt iergendeen eng besser Erklärung wéi dëse Code vu mir funktionnéiert ...

Source: will.com

Setzt e Commentaire