QEMU.js: naha cidî û bi WASM

Carekê min ji bo kêfê biryar da vegerandina pêvajoyê îspat bike û fêr bibin ka meriv çawa JavaScript-ê (bi rastî, Asm.js) ji koda makîneyê biafirîne. QEMU ji bo ceribandinê hate hilbijartin, û demek şûnda gotarek li ser Habr hate nivîsandin. Di şîroveyan de ji min re hate şîret kirin ku ez projeyê li WebAssembly ji nû ve bikim, û tewra jî dev ji xwe berdim hema qediya Min bi rengekî proje nexwest... Kar diçû, lê pir hêdî, û nuha, vê dawiyê di wê gotarê de xuya bû şîrove li ser mijara "Ji ber vê yekê her tişt çawa bi dawî bû?" Di bersiva bersiva min a berfireh de, min bihîst "Ev wekî gotarek xuya dike." Welê, heke hûn bikarin, dê gotarek hebe. Dibe ku kesek wê kêrhatî bibîne. Ji wê xwendevan dê hin rastiyan di derbarê sêwirana paşnavên nifşê koda QEMU de fêr bibe, û hem jî meriv çawa berhevkarek Just-in-Time ji bo serîlêdanek malperê binivîse.

erkên

Ji ber ku min berê fêr bû ku meriv çawa "bi rengekî" QEMU li JavaScript-ê veguhezîne, vê carê biryar hate girtin ku wê bi aqilmendî bikim û xeletiyên kevn dubare neke.

Xeletiya hejmar yek: şaxek ji berdana xalê

Çewtiya min a yekem ev bû ku guhertoya xwe ji guhertoya jorîn 2.4.1 qut bikim. Wê hingê ji min re ramanek baş xuya bû: ger berdana xalê hebe, wê hingê ew belkî ji 2.4-ya sade aramtir e, û hê bêtir şax master. Û ji ber ku min plan kir ku ez hêjmarek ji xeletiyên xwe zêde bikim, qet hewcedariya min bi kesek din tune. Belkî wisa derketiye holê. Lê li vir tişt ev e: QEMU raweste, û di demekê de wan jî xweşbînkirina koda hatî çêkirin ji sedî 10 ragihand. "Erê, naha ez ê bicemidim," min fikir kir û şikest. Li vir pêdivî ye ku em veqetînek bikin: ji ber xwezaya yek-têlkirî ya QEMU.js û rastiya ku QEMU-ya orîjînal nayê wateya nebûna pir-têlankirinê (ango, şiyana xebitandina çend rêyên kodê yên negirêdayî hevdemî, û ne tenê "hemî kernelan bikar bînin") ji bo wê krîtîk e, fonksiyonên sereke yên têlan min neçar kir ku "wê vegerînim" da ku bikaribim ji derve bang bikim. Vê yekê di dema yekbûnê de hin pirsgirêkên xwezayî çêkir. Lê belê, rastiya ku hin guhertin ji şaxê master, ya ku min hewl da ku koda xwe bi hev ve bikim, di berdana xalê de (û ji ber vê yekê di şaxê min de) kiraz hatin hilanîn jî dibe ku rehetiya xwe zêde nekira.

Bi gelemperî, min biryar da ku hîn jî maqûl e ku meriv prototîpê bavêje, wê ji bo perçeyan veqetîne û guhertoyek nû ji nû ve li ser bingeha tiştek nûtir ava bike û naha ji master.

Şaşiya jimare du: Metodolojiya TLP

Di eslê xwe de, ev ne xeletiyek e, bi gelemperî, ew tenê taybetmendiyek afirandina projeyekê ye di şert û mercên têgihîştina bêkêmasî de hem "li ku derê û çawa biçin?" û bi gelemperî "em ê bigihîjin wir?" Di van şert û mercan de bernamekirina nebaş vebijarkek rastdar bû, lê, bi xwezayî, min nexwest wê bêhewce dubare bikim. Vê carê min xwest ku ez wiya bi aqilmendî bikim: erkên atomî, guheztina koda hişmendî (û ne "karekterên bêserûber bi hev re xêz bikin heya ku ew berhev bike (bi hişyarî)", wekî ku Linus Torvalds carekê li ser kesek got, li gorî Wikiquote), hwd.

Xeletiya sêyem: ketina nav avê bêyî zanîna ford

Min hîn jî bi tevahî ji vê yekê xilas nebûye, lê naha min biryar da ku ez çu carî riya berxwedanê ya herî kêm bişopînim, û wiya "wek mezinan" bikim, ango, pişta xweya TCG ji sifirê binivîsim, da ku nekim. ji bo ku paşê bêjim, "Erê, ev bê guman, hêdî hêdî, lê ez nikarim her tiştî kontrol bikim - TCI bi vî rengî tête nivîsandin ..." Wekî din, ev di destpêkê de wekî çareseriyek eşkere xuya bû, ji ber ku Ez koda binary çêdikim. Wekî ku dibêjin, "Gent civiyaу, lê ne ew yek": kod, bê guman, binar e, lê kontrol nikare bi hêsanî jê re were veguheztin - divê ew bi eşkereyî ji bo berhevkirinê di gerokê de were avêtin, ku di encamê de tiştek ji cîhana JS-ê derkeve, ku hîn jî hewce dike li cihekî xilas bibin. Lêbelê, li ser mîmariyên RISC yên normal, bi qasî ku ez fêm dikim, rewşek tîpîk hewce ye ku bi eşkere cache rêwerzê ji bo koda ji nû ve nûvekirî ji nû ve were vegerandin - heke ev ne ya ku em hewce ne, wê hingê, di her rewşê de, ew nêzîk e. Wekî din, ji hewildana xweya paşîn, ez fêr bûm ku kontrol xuya nake ku di nîvê bloka wergerê de were veguheztin, ji ber vê yekê em bi rastî ne hewce ne ku bitekodê ji her cûrbecûr were şîrove kirin, û em dikarin wê bi hêsanî ji fonksiyona li ser TB-ê biafirînin. .

Hatin û pê xistin

Her çend min di Tîrmehê de dest bi ji nû ve nivîsandina kodê kir jî, lêdanek efsûnî ji nedîtî ve hat: bi gelemperî nameyên ji GitHub wekî agahdariya bersivên Pirsgirêkan û Daxwazên Bikişînê têne, lê li vir, nişkê di têlê de behs bike Binaryen wek pişta qemu di çarçoveyê de, "Wî tiştek wusa kir, dibe ku ew tiştek bêje." Me li ser karanîna pirtûkxaneya têkildar a Emscripten dipeyivî Binaryen ji bo afirandina WASM JIT. Welê, min got ku we li wir destûrnameyek Apache 2.0 heye, û QEMU bi tevahî di bin GPLv2 de tê belav kirin, û ew ne pir lihevhatî ne. Ji nişkê ve derket holê ku destûrnameyek dikare bibe bi awayekî rast bikin (Ez nizanim: dibe ku wê biguhezîne, dibe ku lîsansa dualî, dibe ku tiştek din ...). Vê yekê, bê guman, ez kêfxweş kirim, ji ber ku wê demê min berê ji nêz ve lê nêrî formata binary WebAssembly, û ez bi rengek xemgîn û nefêm bûm. Di heman demê de pirtûkxaneyek jî hebû ku dê blokên bingehîn bi grafiya veguhêz dixwar, bytekodê hildiberand, û tewra wê di werger bixwe de jî, ger hewce bike, bimeşîne.

Piştre bêtir bû tîpek li ser navnîşa posta QEMU, lê ev bêtir li ser pirsa, "Ji kê re jî hewce dike?" Û ew e nişkê, derket holê ku ew pêdivî ye. Bi kêmanî, hûn dikarin îmkanên karanîna jêrîn bi hev re bişopînin, heke ew kêm-zêde zû bixebite:

  • destpêkirina tiştek perwerdehiyê bêyî sazkirinê
  • virtualîzasyona li ser iOS-ê, ku, li gorî gotegotan, yekane serîlêdana ku mafê hilberîna kodê li ser firînê heye motorek JS ye (ev rast e?)
  • xwenîşandana mini-OS - yek-floppy, çêkirî, her cûre firmware, hwd...

Taybetmendiyên Runtime Browser

Wekî ku min berê jî got, QEMU bi multithreading ve girêdayî ye, lê gerok wê tune. Belê, ev e, na... Di destpêkê de ew qet tune bû, dûv re WebWorkers xuya bûn - bi qasî ku ez fam dikim, ev li ser bingeha derbaskirina peyamê pirtirs e bê guherbarên hevpar. Bi xwezayî, ev gava ku koda heyî li ser bingeha modela bîranîna hevpar vediguhezîne pirsgirêkên girîng diafirîne. Piştre, di bin zexta raya giştî de, di bin navê de jî hat bicihkirin SharedArrayBuffers. Ew gav bi gav hate destnîşan kirin, wan di gerokên cihêreng de destpêkirina wê pîroz kirin, dûv re sersala nû pîroz kirin û dûv re jî Meltdown... Piştî vê yekê ew gihîştin wê encamê ku pîvandina demê bi qehwet an qelew, lê bi alîkariya bîranîna hevpar û a Mijara ku jimarvan zêde dike, her yek e ew ê pir rast bixebite. Ji ber vê yekê me multithreading bi bîranîna hevbeş neçalak kir. Wusa dixuye ku wan paşê ew vegerandin, lê, wekî ku ji ceribandina yekem diyar bû, jiyan bêyî wê heye, û heke wusa be, em ê hewl bidin ku wiya bêyî ku xwe bispêrin pirtextê bikin.

Taybetmendiya duyemîn nemimkûniya manîpulasyonên di asta nizm de bi stakê re ye: hûn nekarin bi tenê hilînin, çarçoweya heyî hilînin û bi stûnek nû veguherînin yek nû. Stack bang ji hêla makîneya virtual JS ve tê rêvebirin. Wusa dixuye, pirsgirêk çi ye, ji ber ku me hîn jî biryar da ku herikîna berê bi tevahî bi destan birêve bibin? Rastî ev e ku bloka I/O di QEMU de bi navgîniya kortînan ve tête bicîh kirin, û li vir e ku manipulasyonên stakê yên asta nizm bi kêr tê. Xweşbextane, Emscipten jixwe mekanîzmayek ji bo operasyonên asynchronous heye, tewra du jî: Asyncify и Emterpreter. Ya yekem di koda JavaScriptê ya hatî hilberandin de bi blotek girîng dixebite û êdî nayê piştgirî kirin. Ya duyemîn "rêya rast" a heyî ye û ji bo wergêra xwemalî bi hilberîna bytecode dixebite. Ew, bê guman, hêdî-hêdî dixebite, lê ew kodê dişewitîne. Rast e, ji bo vê mekanîzmayê piştgirî ji bo korutinan diviyabû ku bi rengek serbixwe were beşdar kirin (jixwe ji bo Asyncify kortînî hatibûn nivîsandin û ji bo Emterpreter hema hema heman API-yê pêk hat, hûn tenê hewce ne ku wan girêdin).

Heya nuha, min hîn nekariye kodê ku di WASM-ê de hatî berhev kirin û bi karanîna Emterpreter ve hatî şîrove kirin veqetînim, ji ber vê yekê amûrên blokê hîn naxebitin (di rêza paşîn de bibînin, wekî ku dibêjin ...). Ango, di dawiyê de divê hûn tiştek mîna vê tiştê qat-tewreş bistînin:

  • bloka I/O şirove kirin. Welê, we bi rastî hêvî dikir ku NVMe-ya emulkirî bi performansa xwemalî? 🙂
  • Koda sereke ya QEMU ya statîk berhevkirî (werger, amûrên din ên emûlkirî, hwd.)
  • koda mêvan bi dînamîk di WASM de hatî berhev kirin

Taybetmendiyên çavkaniyên QEMU

Wekî ku we berê jî texmîn kir, koda ji bo nimûnekirina mîmariya mêvanan û koda ji bo çêkirina rêwerzên makîneya mêvandar di QEMU de têne veqetandin. Di rastiyê de, ew hinekî dijwartir e jî:

  • mîmariyên mêvan hene
  • e accelerators, ango, KVM ji bo virtualkirina hardware li ser Linux (ji bo pergalên mêvan û mêvandar ên ku bi hevûdu re hevaheng in), TCG ji bo hilberîna koda JIT li her deverê. Bi QEMU 2.9-ê dest pê kir, piştgirî ji bo standarda virtualîzasyona hardware HAXM li ser Windows xuya bû (hûrguliyên)
  • heke TCG were bikar anîn û ne virtualîzasyona hardware, wê hingê ji bo her mîmariya mêvandar, û her weha ji bo wergêrê gerdûnî piştgirîya hilberîna kodê ya cihêreng heye.
  • ... û li dora van hemûyan - periferîkên emulkirî, navrûya bikarhêner, koçberî, tomar-replay, hwd.

Bi awayê, we dizanibû: QEMU dikare ne tenê tevahiya komputerê, lê di heman demê de pêvajoyek ji bo pêvajoyek bikarhênerek cihêreng a di kernelê mêvandar de, ku ji hêla fuzzera AFL-ê ve ji bo amûrên binaryê ve tê bikar anîn, mîna nimûne bike. Dibe ku kesek bixwaze vê awayê xebitandina QEMU ji JS-ê re bike? 😉

Mîna pir nermalava belaş a demdirêj, QEMU bi bangê ve hatî çêkirin configure и make. Ka em bibêjin ku hûn biryar didin ku tiştek lê zêde bikin: paşvekêşek TCG, bicîhkirina mijarê, tiştek din. Lez nekin ku hûn dilgeş / tirsnak bin (wekî minasib binxêz bikin) li ser perspektîfa danûstendina bi Autoconf re - bi rastî, configure QEMU bi eşkere xwe-nivîsandî ye û ji tiştek nayê çêkirin.

Tevlêbûn Tevne

Ji ber vê yekê ev tişt bi navê WebAssembly (aka WASM) çi ye? Ev li şûna Asm.js e, êdî xwe wekî koda JavaScriptê derbasdar nake. Berevajî vê, ew bi tenê binar û xweşbîn e, û tewra bi tenê nivîsandina jimareyek tê de ne pir hêsan e: ji bo kompaktbûnê, ew di formê de tête hilanîn. LEB128.

Dibe ku we li ser algorîtmaya vegerandinê ya ji bo Asm.js bihîstibe - ev nûvekirina rêwerzên kontrolkirina herikînê "asta bilind" e (ango, heke-paşê-din, lûf, hwd.), ku motorên JS-ê ji bo wê hatine sêwirandin, ji LLVM IR-a asta nizm, ji koda makîneyê ya ku ji hêla pêvajoyê ve hatî darve kirin nêzîktir e. Bi xwezayî, nûnertiya navîn a QEMU nêzîkê duyemîn e. Wisa dixuye ku ew li vir e, bytecode, dawiya ezabê... Û dû re jî blokên, ger-pa-dî-dî- û xelek hene!..

Û ev sedemek din e ku Binaryen bikêr e: ew bi xwezayî dikare blokên astek bilind ên nêzî tiştê ku dê di WASM de were hilanîn qebûl bike. Lê di heman demê de ew dikare kodê ji grafiyek blokên bingehîn û veguheztina di navbera wan de jî hilberîne. Welê, min berê jî got ku ew formata hilanînê WebAssembly li pişt C/C++ API-ya hêsan vedişêre.

TCG (Tiny Code Generator)

GTC bi eslê xwe bû paşîn ji bo berhevkarê C. Dûv re, xuya ye, ew nekariye pêşbirka bi GCC re bisekine, lê di dawiyê de cîhê xwe di QEMU de wekî mekanîzmayek hilberîna kodê ji bo platforma mêvandar dît. Di heman demê de paşnavek TCG jî heye ku hin bytekodên razber çêdike, ku tavilê ji hêla wergêr ve tê darve kirin, lê min biryar da ku vê carê ji karanîna wê dûr bixim. Lêbelê, rastiya ku di QEMU de jixwe gengaz e ku bi fonksiyonê veguheztina TB-ya hatî hilberandin çalak bike. tcg_qemu_tb_exec, ji min re pir bikêr derket.

Ji bo ku paşnavek nû ya TCG-ê li QEMU zêde bikin, hûn hewce ne ku binerdekek biafirînin tcg/<имя архитектуры> (di vê rewşê de, tcg/binaryen), û du pelan dihewîne: tcg-target.h и tcg-target.inc.c и şertavêtin her tişt li ser e configure. Hûn dikarin pelên din li wir bixin, lê, wekî ku hûn dikarin ji navên van her duyan texmîn bikin, ew ê her du jî li cîhek bêne nav kirin: yek wekî pelek sernavê ya birêkûpêk (ew di nav de ye tcg/tcg.h, û ew yek jixwe di pelên din ên peldankan de ye tcg, accel û ne tenê), ya din - tenê wekî kodek kodek tê de tcg/tcg.c, lê gihîştina fonksiyonên wê yên statîk heye.

Biryar da ku ez ê pir wext li ser vekolînên hûrgulî yên ka ew çawa dixebite derbas bikim, min bi tenê "îskeletên" van her du pelan ji pêkanîna paşîn a din kopî kir, bi dilsozî vê yekê di sernavê lîsansê de destnîşan kir.

file tcg-target.h bi piranî mîhengên di formê de vedihewîne #define-s:

  • çend tomar û çi firehî li ser mîmariya armancê hene (em bi qasî ku em dixwazin, bi qasî ku em dixwazin hene - pirs bêtir li ser wê ye ku dê ji hêla gerokê ve li ser mîmariya "bi tevahî armanc" di kodek bikêrtir de çi were çêkirin. ...)
  • lihevkirina rêwerzên mêvandar: li ser x86, û tewra di TCI de, rêwerzan bi tevahî ne lihevkirî ne, lê ez ê di tampona kodê de ne talîmatan bihêlim, lê nîgarên strukturên pirtûkxaneya Binaryen, ji ber vê yekê ez ê bibêjim: 4 bytes
  • çi rêwerzên vebijarkî ku paşverû dikare çêbike - em her tiştê ku em di Binaryen de dibînin vedihewînin, bihêlin bilezker mayî bi xwe bişkîne yên hêsan
  • Mezinahiya nêzîkê cacheya TLB ya ku ji hêla paşîn ve hatî xwestin çi ye. Rastî ev e ku di QEMU de her tişt ciddî ye: her çend fonksiyonên arîkar hene ku li gorî mêvanê MMU-yê barkirin/firoşgeh pêk tînin (em ê niha bêyî wê li ku bin?), ew cache wergera xwe di forma avahiyek de hilînin, pêvajoyek ku hêsan e ku meriv rasterast di blokên weşanê de bicîh bike. Pirs ev e, di vê avahîsaziyê de çi jihevdexistin bi rêzek piçûk û bilez a fermanan bi herî bikêr tê pêvajo kirin?
  • li vir hûn dikarin armanca yek an du tomarên veqetandî biguhezînin, bi fonksiyonek ve bangkirina TB-yê çalak bikin û bi vebijarkî çend hûrguliyên piçûk diyar bikin. inline- fonksiyonên mîna flush_icache_range (lê ev ne doza me ye)

file tcg-target.inc.c, bê guman, bi gelemperî bi mezinahî pir mezintir e û çend fonksiyonên mecbûrî dihewîne:

  • destpêkkirin, di nav de sînorkirinên li ser kîjan talîmatan dikarin li ser kîjan operatoran bixebitin. Bi eşkere ji hêla min ve ji paşverûyek din ve hatî kopî kirin
  • fonksiyona ku yek rêwerzek bytecode ya navxweyî digire
  • Her weha hûn dikarin fonksiyonên alîkar li vir bicîh bikin, û hûn dikarin ji fonksiyonên statîk jî bikar bînin tcg/tcg.c

Ji bo xwe, min stratejiya jêrîn hilbijart: di peyvên pêşîn ên bloka wergera paşîn de, min çar nîgaran nivîsand: nîşanek destpêk (nirxek diyar li derdorê 0xFFFFFFFF, ku rewşa heyî ya TB-ê diyar kir, çarçove, modulek çêkirî, û jimareya efsûnî ya ji bo xeletkirinê. Di destpêkê de nîşanek hate danîn 0xFFFFFFFF - nko n - hejmareke pozîtîf biçûk, û her carê ku bi rêya wergêrê dihat îdam kirin, ew 1 zêde bû. 0xFFFFFFFE, berhevkirin pêk hat, modul di tabloya fonksiyonê de hate hilanîn, li "destpêkek" piçûk hate hilanîn, ku tê de darvekirin ji tcg_qemu_tb_exec, û modul ji bîra QEMU hate rakirin.

Ji bo ravekirina klasîkan, "Kirç, çiqas di vî dengî de ji bo dilê pêşgotin ...". Lêbelê, bîranîn li cihekî diherikî. Wekî din, ew bîranîn ji hêla QEMU ve hate rêvebirin! Kodek min hebû ku, dema ku rêwerza paşîn dinivîsand (baş, ango nîşanek), yê ku lînka wî berê li vê derê bû jêbirin, lê ev yek ne alîkar bû. Bi rastî, di rewşa herî hêsan de, QEMU di destpêkê de bîranînê vediqetîne û koda çêkirî li wir dinivîse. Dema ku tampon xilas dibe, kod tê avêtin û ya din li şûna xwe dest bi nivîsandinê dike.

Piştî xwendina kodê, min fêhm kir ku fêlbaziya bi hejmara sêrbaz re hişt ku di derbasbûna yekem de tiştek xelet li ser tamponek bêdestpêk azad bike, di hilweşandina tovê de têk neçe. Lê kî tamponê ji nû ve dinivîse da ku paşê fonksiyona min derbas bike? Wekî ku pêşdebirên Emscripten şîret dikin, gava ku ez ketim pirsgirêkek, min koda encam vedigere serîlêdana xwemalî, Mozilla Record-Replay li ser wê saz kir... Bi gelemperî, di dawiyê de min tiştek hêsan fêm kir: ji bo her blokê, yek struct TranslationBlock bi vegotina xwe. Texmîn bikin ku... Rast e, hema berî bloka rastê di tamponê de. Vê yekê fêm kir, min biryar da ku dev ji karanîna kêzikan berdim (qet nebe hin), û bi tenê jimara sêrbaz avêt, û peyvên mayî veguhast struct TranslationBlock, lîsteyek girêdayek yekalî diafirîne ku dikare zû were derbas kirin dema ku cacheya wergerê ji nû ve were vegerandin, û bîra azad bike.

Hin kêşan dimînin: Mînakî, nîşankerên di tampona kodê de nîşankirî - hin ji wan bi hêsanî ne BinaryenExpressionRef, ango, ew li biwêjên ku divê bi xêzikî di bloka bingehîn a hatî çêkirin de werin danîn dinêrin, beşek şertê derbasbûna di navbera BB-yan de ye, beşek ew e ku biçin ku derê. Welê, jixwe ji bo Relooper blokên amadekirî hene ku hewce ne ku li gorî şertan werin girêdan. Ji bo cihêkirina wan, texmîn tê bikar anîn ku ew hemî bi kêmî ve çar bit li hev hatine, ji ber vê yekê hûn dikarin bi ewlehî du bitên herî kêm girîng ji bo etîketê bikar bînin, hûn tenê hewce ne ku ji bîr mekin ku heke pêwîst be wê jêbirin. Bi awayê, etîketên weha jixwe di QEMU de têne bikar anîn da ku sedema derketina ji çerxa TCG destnîşan bikin.

Bikaranîna Binaryen

Modulên di WebAssembly de fonksiyonan dihewîne, ku her yek ji wan laşek heye, ku îfadeyek e. Gotin operasyonên yekane û binar in, blokên ku ji navnîşên bêjeyên din pêk tên, herikîna kontrolê, hwd. Wekî ku min berê jî got, herikîna kontrolê ya li vir bi rastî wekî şaxên astek bilind, loop, bangên fonksiyonê, hwd tê organîze kirin. Argumentên fonksiyonan ne li ser stackê têne derbas kirin, lê bi eşkere, mîna JS. Guherînên gerdûnî jî hene, lê min ew bi kar neanîne, ji ber vê yekê ez ê ji we re li ser wan nebêjim.

Fonksiyon di heman demê de guhêrbarên herêmî jî hene, ji sifirê têne hejmartin, ji celebê: int32 / int64 / float / double. Di vê rewşê de, n guherbarên herêmî yên yekem argumanên ku ji fonksiyonê re derbas dibin. Ji kerema xwe bala xwe bidin ku her çend li vir di warê herikîna kontrolê de bi tevahî ne di asta nizm de ye jî, hêjmar hîn jî taybetmendiya "îmzakirî/nenîşankirî" hilnagirin: ka hejmar çawa tevdigere bi koda operasyonê ve girêdayî ye.

Bi gelemperî, Binaryen peyda dike hêsan C-API: hûn modulek diafirînin, li wî îfadeyan biafirînin - unary, binary, blokên ji îfadeyên din, herikîna kontrolê, hwd. Dûv re hûn fonksiyonek bi vegotinek wekî laşê wê diafirînin. Ger hûn, mîna min, xwedan grafiyek veguheztinê ya nizm be, dê pêkhateya relooper ji we re bibe alîkar. Bi qasî ku ez fêm dikim, gengaz e ku meriv di blokê de kontrola asta bilind a herikîna darvekirinê bikar bîne, heya ku ew ji sînorên blokê dernekeve - ango, gengaz e ku meriv riya bilez / hêdî ya hundurîn bike. rêyek ku di hundurê koda pêvajoya cache ya TLB-ya çêkirî de şax dibe, lê ne ku mudaxeleyî herikîna kontrolê ya "derveyî" bike. Dema ku hûn relooperek azad dikin, blokên wê têne azad kirin; dema ku hûn modulek azad dikin, biwêj, fonksiyon û hwd ku jê re hatine veqetandin winda dibin. şermeydan.

Lêbelê, heke hûn dixwazin kodê bi lez bê afirandin û jêbirina mînakek werger şîrove bikin, dibe ku maqûl be ku hûn vê mantiqê têxin pelek C++, û ji wir rasterast tevahiya API-ya C++ ya pirtûkxaneyê bi rêve bibin, û amade ne. pêçan çêkir.

Ji ber vê yekê hûn koda ku hûn hewce ne biafirînin

// настроить глобальные параметры (можно поменять потом)
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);

... heke min tiştek ji bîr kir, bibore, ev tenê ji bo temsîlkirina pîvanê ye, û hûrgulî di belgeyê de ne.

Û niha crack-fex-pex dest pê dike, tiştek mîna vê:

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);

Ji bo ku bi rengek din cîhanên QEMU û JS bi hev ve girêbidin û di heman demê de zû bigihîjin fonksiyonên berhevkirî, arrayek hate afirandin (tabloyek fonksiyonan ji bo ketina nav destikê), û fonksiyonên hatî çêkirin li wir hatin danîn. Ji bo bi lez hesabkirina îndeksê, îndeksa bloka wergerandina peyva sifir di destpêkê de wekî wê hate bikar anîn, lê dûv re îndeksa ku bi karanîna vê formulê hatî hesibandin dest pê kir ku bi hêsanî di nav zeviyê de cih bigire. struct TranslationBlock.

Bi awayê demo (niha bi lîsansek tarî) tenê di Firefox de baş dixebite. Pêşdebirên Chrome bûn bi awayekî ne amade ne ji bo wê yekê ku kesek bixwaze zêdetirî hezar nimûneyên modulên WebAssembly biafirîne, ji ber vê yekê ew tenê gigabyte cîhê navnîşana virtual ji bo her yekê veqetandin ...

Ji bo niha ev hemû. Ger kesek eleqedar be, dibe ku gotarek din hebe. Ango, herî kêm dimîne bes cîhazên blokê bixebitin. Di heman demê de dibe ku guncan be ku berhevkirina modulên WebAssembly asynkron be, wekî ku di cîhana JS-ê de normal e, ji ber ku hîn jî wergêrek heye ku dikare van hemîyan bike heya ku modula xwecihî amade nebe.

Di dawiyê de mizgîniyek: we binaryek li ser mîmariya 32-bit berhev kiriye, lê kod, bi navgîniya operasiyonên bîranînê, ji Binaryen, li deverek li ser stakê, an cîhek din di 2 GB ya jorîn a cîhê navnîşana 32-bit de derdikeve. Pirsgirêk ev e ku ji hêla Binaryen ve ev gihîştina navnîşek encamek pir mezin e. Meriv çawa li dora vê yekê bigire?

Bi awayê admin

Min dawî li ceribandina vê nekir, lê ramana min a yekem ev bû "Gelo ez Linux 32-bit saz bikim?" Dûv re beşa jorîn a cîhê navnîşanê dê ji hêla kernel ve were dagir kirin. Pirsa tenê ev e ku çiqas dê were dagir kirin: 1 an 2 Gb.

Bi awayê bernamesaz (vebijarkek ji bo bijîjkan)

Werin em li serê cîhê navnîşanê bilbilek bifirînin. Ez bixwe fam nakim çima ew dixebite - li wir êdî divê stûnek hebe. Lê "em pratîsyen in: her tişt ji bo me dixebite, lê kes nizane çima ..."

// 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));
}

... rast e ku ew bi Valgrind re ne lihevhatî ye, lê, bextewar, Valgrind bixwe pir bi bandor her kesî ji wir derdixe :)

Dibe ku kesek ravekek çêtir bide ka ev koda min çawa dixebite…

Source: www.habr.com

Add a comment