Disa disa vjet mĂ« parĂ«, Fabrice Bellard â njĂ« emulator PC, i shkruar nĂ« JavaScript. Pas kĂ«saj, kishte ndodhur tĂ« paktĂ«n . Por sa mĂ« di unĂ«, tĂ« gjitha ishin interpretime, ndĂ«rsa Qemu, i shkruar mĂ« herĂ«t nga Fabrice Bellard, dhe ndoshta çdo emulator modern i njohur, pĂ«rdor kompilim JIT tĂ« kodit tĂ« mysafirĂ«ve nĂ« kodin e sistemit mik. MĂ« dukej se ishte koha e duhur pĂ«r tĂ« zbatuar detyrĂ«n e kundĂ«rt tĂ« asaj qĂ« zgjidhin shfletuesit: kompilimin JIT tĂ« kodit tĂ« makinĂ«s nĂ« JavaScript, pĂ«r tĂ« cilin ishte mĂ« logjike tĂ« portosh Qemu. Mund tĂ« duket se pse pikĂ«risht Qemu, ka emulatorĂ« mĂ« tĂ« thjeshtĂ« dhe miqĂ«sorĂ« pĂ«r pĂ«rdoruesin â sikur VirtualBox, pĂ«r shembull â sa instalohet dhe punon. Por Qemu ka disa veçori interesante
- kodin burimor të hapur
- mundësinë për të punuar pa driverin e bërthamës
- mundësinë për të punuar në modin e interpreterit
- mbështetje për numër të madh si të arkitekturave mik, ashtu edhe të mysafirëve
NĂ« lidhje me pikĂ«n e tretĂ«, tani mund tĂ« sqaroj se nĂ« fakt, nĂ« modin TCI interpretohen jo vetĂ« instruktionet e makinĂ«s sĂ« mysafirit, por kodin bajt qĂ« rrjedh nga ato, por thelbĂ«sore kjo nuk ndryshon â pĂ«r tĂ« mbledhur dhe nisur Qemu nĂ« njĂ« arkitekturĂ« tĂ« re, nĂ«se ke fat, mjafton njĂ« kompilues C â shkruajtja e gjeneruesit tĂ« kodit mund tĂ« shtyhet.
Dhe ja, pas dy vjetësh në një proces të ngadalshëm të zbulimit të kodit burimor të Qemu, u shfaq një prototip funksional, në të cilin mund të nisësh, për shembull, Kolibri OS.
ĂfarĂ« Ă«shtĂ« Emscripten
NĂ« kohĂ«n tonĂ« kanĂ« dalĂ« shumĂ« kompilues, rezultati pĂ«rfundimtar i tĂ« cilĂ«ve Ă«shtĂ« JavaScript. Disa, si Type Script, u menduan nga fillimi si njĂ« mĂ«nyrĂ« mĂ« e mirĂ« pĂ«r tĂ« shkruar pĂ«r webin. NdĂ«rkohĂ«, Emscripten Ă«shtĂ« njĂ« mĂ«nyrĂ« pĂ«r tĂ« marrĂ« kodin ekzistues nĂ« C ose C++, dhe ta kompilosh nĂ« njĂ« format tĂ« kuptueshĂ«m nga shfletuesi. NĂ« janĂ« mbledhur shumĂ« porte tĂ« programeve tĂ« njohura: , pĂ«r shembull, mund tĂ« shikoni mbi PyPy â pĂ«r tĂ« cilin, siç thuhet, ata tashmĂ« kanĂ« JIT. NĂ« tĂ« vĂ«rtetĂ«, jo çdo program mund tĂ« kompilhet thjesht dhe tĂ« niset nĂ« shfletues â ekziston njĂ« sĂ«rĂ« , me tĂ« cilat duhet tĂ« pajtohesh, siç thotĂ« edhe mbishkrimi nĂ« kĂ«tĂ« faqe "Emscripten mund tĂ« pĂ«rdoret pĂ«r tĂ« kompiluar pothuajse çdo portable Kodi C/C++ nĂ« JavaScript. Kjo do tĂ« thotĂ« se ekziston njĂ« numĂ«r operacionesh qĂ« janĂ« sjellje tĂ« pacaktuara sipas standardit, por zakonisht funksionojnĂ« nĂ« x86 â pĂ«r shembull, qasja e pakorrigjuar nĂ« variablat, e cila nĂ« disa arkitektura Ă«shtĂ« krejtĂ«sisht e ndaluar. NĂ« pĂ«rgjithĂ«si, Qemu Ă«shtĂ« njĂ« program multidimensional dhe, shpresoja, nuk ka shumĂ« sjellje tĂ« pacaktuara â merrni dhe kompilo, pastaj pak punĂ« me JIT â dhe Ă«shtĂ« gati! Por nuk ishte aq e thjeshtĂ«...
Përpjekja e parë
NĂ« tĂ« vĂ«rtetĂ«, unĂ« nuk jam i pari qĂ« i ka ardhur ideja pĂ«r tĂ« portuar Qemu nĂ« JavaScript. NĂ« forumet e ReactOS u bĂ« njĂ« pyetje, nĂ«se Ă«shtĂ« e mundur kjo me Emscripten. MĂ« herĂ«t kanĂ« qarkulluar thashetheme se kĂ«tĂ« e kishte bĂ«rĂ« personalisht Fabrice Bellard, por ishte pĂ«r jslinux, i cili, sa di unĂ«, nĂ« fakt Ă«shtĂ« njĂ« pĂ«rpjekje pĂ«r tĂ« arritur performancĂ« tĂ« mjaftueshme nĂ« JS, dhe Ă«shtĂ« shkruar nga fillimi. MĂ« vonĂ« u shkrua Virtual x86 â atij iu publikuan burimet e pakomprometuara, dhe siç u tha, "realizimi" mĂ« i madh i emulimit e mundĂ«soi pĂ«rdorimin e SeaBIOS si firmware. PĂ«r mĂ« tepĂ«r, kishte sĂ« paku njĂ« pĂ«rpjekje pĂ«r tĂ« portuar Qemu me Emscripten â kjo pĂ«rpjekje ishte bĂ«rĂ« , por zhvillimi, sa kuptoj, u pezullua.
KĂ«shtu, duke u dukur se, ja burimet, ja Emscripten â merr dhe kompilo. Por ka gjithashtu biblioteka nga tĂ« cilat Qemu varet, dhe biblioteka nga tĂ« cilat varen ato biblioteka e kĂ«shtu me radhĂ«, pĂ«rveç qĂ« njĂ« nga to Ă«shtĂ« â , nga e varet glib. NĂ« internet ishin pĂ«rhapur thashetheme se nĂ« njĂ« koleksion tĂ« madh portesh bibliotekash pĂ«r Emscripten kishte dhe tĂ« tillĂ«, por nuk besohej shumĂ«: sĂ« pari, me kompilatorin e ri nuk mund tĂ« ishte mbledhur, sĂ« dyti, kjo Ă«shtĂ« njĂ« bibliotekĂ« shumĂ« e nivelit tĂ« ulĂ«t, pĂ«r tĂ« marrĂ« dhe mbledhur thjesht nĂ« JS. Dhe problemi nuk Ă«shtĂ« vetĂ«m me sĂ«muese gjuhĂ«s â ndoshta, nĂ«se e shpĂ«rndajmĂ« disi, pĂ«r disa konvensionet e thirrjes, mund tâi formojmĂ« argumentet e nevojshme nĂ« stek dhe tĂ« thĂ«rrasim funksionin. Por Emscripten Ă«shtĂ« njĂ« gjĂ« e ndĂ«rlikuar: pĂ«r tĂ« bĂ«rĂ« qĂ« kodi i gjeneruar tĂ« duket iu njohur optimizuesit tĂ« motorit JS tĂ« shfletuesit, pĂ«rdoren disa truke. NĂ« veçanti, kĂ«shtu quhet rikonfigurimi â gjeneratori i kodit pĂ«r IR-nĂ« LLVM tĂ« marrĂ« me disa instrukcione abstrakte kalimesh pĂ«rpiqet tĂ« rikrijojĂ« if-e tĂ« besueshĂ«m, cikle etj. Dhe si kalohen argumentet nĂ« funksion? Natyrisht, si argumentet e funksioneve JS, pra, sa mĂ« shumĂ« qĂ« Ă«shtĂ« e mundur, jo pĂ«rmes stekut.
NĂ« fillim kisha mendimin thjesht tĂ« shkruaja njĂ« zĂ«vendĂ«sim pĂ«r libffi nĂ« JS dhe tĂ« kaloja testet standarde, por pĂ«rfundimisht u ngatĂ«rrua me mĂ«nyrĂ«n se si tĂ« bĂ«ja skedarĂ«t e mi tĂ« titujve qĂ« tĂ« funksiononin me kodin ekzistues â çfarĂ« tĂ« bĂ«j, siç thuhet, "QoftĂ« detyrat kaq tĂ« komplikuara, qoftĂ« ne kaq tĂ« trashĂ«". MĂ« duhej tĂ« portoja libffi nĂ« njĂ« arkitekturĂ« tjetĂ«r, nĂ«se mund ta shpreh kĂ«shtu â pĂ«r fat tĂ« mirĂ«, nĂ« Emscripten ka si makros pĂ«r asamble inline (nĂ« javascript, po â si çdo arkitekturĂ«, asambleja e saj), ashtu edhe mundĂ«sinĂ« pĂ«r tĂ« ekzekutuar kodin e gjeneruar nĂ« fluk tĂ« parĂ«. NĂ« pĂ«rgjithĂ«si, pas ca kohĂ«sh punĂ« me fragmentet e varura nga platforma tĂ« libffi, kam marrĂ« njĂ« kod qĂ« mund tĂ« kompiloj, dhe e kalova atĂ« nĂ« provĂ«n e parĂ« qĂ« mĂ« doli pĂ«rpara. PĂ«r habinĂ« time, testi kaloi me sukses. I befasuar nga gjenialiteti im â a Ă«shtĂ« shaka, punoi me startin e parĂ« â unĂ«, ende pa e besuar sytĂ« e mi, doja tĂ« shikoja akoma njĂ« herĂ« kodin e rezultuar, tĂ« vlerĂ«soja se ku tĂ« gĂ«rmoj mĂ« tej. KĂ«tu u befasova pĂ«r herĂ« tĂ« dytĂ« â e vetmja gjĂ« qĂ« bĂ«nte funksioni im ffi_call â ishte qĂ« lajmĂ«ronte pĂ«r njĂ« thirrje tĂ« suksesshme. E thirrjes vetĂ« nuk kishte. KĂ«shtu dĂ«rgova kĂ«rkesĂ«n time tĂ« parĂ« pĂ«r pull, e cila korrigjoi njĂ« gabim tĂ« dukshĂ«m pĂ«r çdo olimpist nĂ« test â numrat realĂ« nuk duhet tĂ« krahasohen si a == b dhe madje as si a - b < EPS â nuk duhet tĂ« harrojmĂ« edhe modulin, pĂ«rndryshe 0 do tĂ« bĂ«het me tĂ« vĂ«rtetĂ« 1/3⊠NĂ« pĂ«rgjithĂ«si, kam krijuar njĂ« port tĂ« libffi, i cili kalon testet mĂ« tĂ« thjeshta dhe me tĂ« cilin kompilohet glib â vendosa, nĂ«se do tĂ« jetĂ« e nevojshme, mĂ« vonĂ« do ta shkruaj mĂ« tej. Po them paraprakisht se, siç u duk, kompajleri madje nuk e pĂ«rfshiu kodin pĂ«rfundimtar tĂ« funksionit libffi.
Por, siç e thashĂ« mĂ« parĂ«, ka disa kufizime, dhe mes pĂ«rdorimit tĂ« lirĂ« tĂ« sjelljeve tĂ« ndryshme tĂ« pasigurt ka njĂ« veçori tĂ« pakĂ«ndshme â JavaScript me dizajn nuk mbĂ«shtet shumĂ«fishtĂ« me kujtesĂ« tĂ« pĂ«rbashkĂ«t. NĂ« thelb, kjo zakonisht mund tĂ« quhet njĂ« ide e mirĂ«, por jo pĂ«r portimin e kodit, qĂ« arkitektura e tij Ă«shtĂ« e ndĂ«rlidhur me thread-et e C. NĂ« pĂ«rgjithĂ«si, nĂ« Firefox janĂ« duke u kryer eksperimente pĂ«r mbĂ«shtetje tĂ« punonjĂ«sve tĂ« ndarĂ«, dhe implementimi i pthread pĂ«r ta nĂ« Emscripten Ă«shtĂ« i pranishĂ«m, por nuk doja tĂ« varesha nga kjo. Isha detyruar gradualisht tĂ« hiqja shumĂ«fishtĂ« nga kodi i Qemu â dmth. tĂ« gjej se ku nisin thread-at, tĂ« nxjerr trupi i ciklit qĂ« ekzekutohet nĂ« kĂ«tĂ« thread nĂ« njĂ« funksion tĂ« veçantĂ« dhe tĂ« thĂ«rras me radhĂ« kĂ«to funksione nga cikli kryesor.
Përpjekja e dytë
NĂ« njĂ« moment u bĂ« e qartĂ« se gjithçka ishte nĂ« vendin e vet, dhe se shpĂ«rndarja kaotike e 'kollive' nĂ« kode nuk do tĂ« sillte asgjĂ« tĂ« mirĂ«. PĂ«rfundimi: duhet ndonjĂ« mĂ«nyrĂ« pĂ«r tĂ« sistematizuar procesin e shtimit tĂ« 'kollive'. Prandaj u mor versioni i ri nĂ« atĂ« moment 2.4.1 (jo 2.5.0, sepse, kush e di, mund tĂ« ketĂ« ende ndonjĂ« defekt tĂ« paparĂ« nĂ« versionin e ri, dhe mjaft Ă«shtĂ« tĂ« kem defektet e mia), dhe e para e punĂ«s ishte tĂ« rishkruhej nĂ« njĂ« mĂ«nyrĂ« tĂ« sigurt. thread-posix.c. Pra, si tĂ« sigurt: nĂ«se dikush pĂ«rpiqej tĂ« kryente njĂ« operacion qĂ« shkaktonte bllokim, menjĂ«herĂ« thirrej funksioni abort() â natyrisht, kjo nuk zgjidhte tĂ« gjitha problemet menjĂ«herĂ«, por tĂ« paktĂ«n, ishte mĂ« e kĂ«ndshme sesa tĂ« merrje qetĂ«sisht njĂ« inkonsistencĂ« tĂ« tĂ« dhĂ«nave.
NĂ« pĂ«rgjithĂ«si, opsionet Emscripten ndihmojnĂ« shumĂ« nĂ« portimin e kodit nĂ« JS -s ASSERTIONS=1 -s SAFE_HEAP=1 â ato kapin disa lloje tĂ« sjelljeve tĂ« pasigurt siç janĂ« qasjet nĂ« adresat e pambĂ«shtetura (çfarĂ« nuk pĂ«rputhet fare me kodin pĂ«r tabulat e tipizuara siç Ă«shtĂ« HEAP32[addr >> 2] = 1) ose thirrjen e njĂ« funksioni me numrin e gabuar tĂ« argumenteve.
MegjithatĂ«, gabimet e rregullimit janĂ« njĂ« temĂ« e veçantĂ«. Siç kam thĂ«nĂ«, nĂ« Qemu ka njĂ« kod tĂ« interpretuar "tĂ« degjeneruar" tĂ« gjenerimit tĂ« kodit TCI (tiny code interpreter), dhe pĂ«r tĂ« ndĂ«rtuar dhe ekzekutuar Qemu nĂ« njĂ« arkitekturĂ« tĂ« re, nĂ«se ke fat, mjafton kompajleri C. FjalĂ«t kyçe "nĂ«se ke fat". Mua nuk mĂ« ndodhi fat, dhe u zbulua se TCI gjatĂ« analizes sĂ« kodit tĂ« tij bajt pĂ«rdor njĂ« akses tĂ« pa rregulluar. Pra, nĂ« çdo arkitekturĂ« si ARM dhe tĂ« tjera qĂ« kĂ«rkojnĂ« akses tĂ« rregulluar, Qemu kompiloi sepse pĂ«r to ka njĂ« TCG-bekend normal, qĂ« gjeneron kod natyror, ndĂ«rsa nĂ«se TCI do tĂ« funksiononte nĂ« to â kjo Ă«shtĂ« njĂ« tjetĂ«r çështje. MegjithatĂ«, siç u duk, nĂ« dokumentacionin e TCI ishte e qartĂ« se diçka e tillĂ« ishte e shĂ«nuar. NĂ« fund, nĂ« kod u shtuan thirrje funksionesh pĂ«r lexim tĂ« pa rregulluar, qĂ« u zbuluan nĂ« njĂ« pjesĂ« tjetĂ«r tĂ« Qemu.
Shkatërrimi i grumbullit
Në fund, aksesi i pa rregulluar në TCI u korrigjua, u krijua cikli kryesor, që thirri radhazi procesorin, RCU dhe diçka tjetër të vogël. Dhe ja ku ekzekutoj Qemu me opsionin -d exec,in_asm,out_asm, që do të thotë se duhet të flasim për cilat blloqe kodi po ekzekutohen, si dhe në momentin e përkthimit të shkruajmë se cili kod mysafir ishte, cili kod host u bë (në këtë rast, bajt kodi). Ai fillon, ekzekuton disa бlloqe përkthimi, shkruan mesazhin tim të rezultateve që tani do të ekzekutohet RCU dhe⊠ra në abort() brenda funksionit free(). Duke e shqetësuar funksionin free() u arrit të zbulohesha se në kokën e bllokut të grumbullit, që ndodhet në tetë bajta para memorjes të alokuar, përveç dimensionit të bllokut ose diçkaje të ngjashme doli të ishte mbetje.
ShkatĂ«rrimi i grumbullit - si e bukur... NĂ« njĂ« rast tĂ« tillĂ« ka njĂ« mjet tĂ« dobishĂ«m - nga (sa mĂ« shumĂ« tĂ« jetĂ« e mundur) tĂ« njĂ«jtit burime tĂ« pĂ«rpilosh njĂ« binar natyral dhe ta testosh me Valgrind. Pas njĂ« kohe, binari ishte gati. E ndizja me tĂ« njĂ«jtat opsione - binte akoma nĂ« fillim, pa arritur tĂ« ekzekutohej. Sigurisht, e papĂ«lqyeshme - duket se burimet nuk ishin krejt tĂ« njĂ«jtat, çka nuk Ă«shtĂ« befasuese, pasi configure kishte zbuluar disa opsione tĂ« tjera, por unĂ« e kam Valgrind - sĂ« pari do ta rregulloj kĂ«tĂ« gabim, dhe pastaj, nĂ«se kam fat, do tĂ« shfaqet edhe ai burim. E ndizja tĂ« njĂ«jtĂ«n gjĂ« me Valgrind... Wow, po, ajo u ndez, kaloi fillimin normalisht dhe shkoi mĂ« tej pĂ«rmes gabimit burimor pa asnjĂ« paralajmĂ«rim pĂ«r qasje tĂ« gabuar nĂ« memorje, pa pĂ«rmendur rĂ«niet. Jeta, siç thonĂ«, nuk mĂ« kishte pĂ«rgatitur pĂ«r njĂ« gjĂ« tĂ« tillĂ« - njĂ« program qĂ« binte nuk bie kur nisi me Valgrind. ĂfarĂ« ishte kjo - njĂ« mister. Hipoteza ime Ă«shtĂ« se pĂ«r shkak tĂ« njĂ« pointeri tĂ« vlefshĂ«m duke pĂ«rdorur ose register-in memset-a me njĂ« pointer tĂ« vlefshĂ«m duke pĂ«rdorur ose mmx, ose xmm regjistrat, ndoshta kjo ishte njĂ« gabim i ndjeshmĂ«risĂ«, megjithatĂ«, prapĂ« Ă«shtĂ« e vĂ«shtirĂ« pĂ«r t'u besuar.
Ok, Valgrind here seems to be no help. And here began the most unpleasant part â everything seems to start, but it crashes for absolutely unknown reasons due to an event that could have happened millions of instructions ago. For a long time, it was unclear how to approach it. Eventually, I had to sit down and debug. Printing what was rewritten to the header showed that it looked not like a number, but more like some binary data. And, oh wonder, this binary string was found in the BIOS file â meaning it could now be said with enough confidence that it was a buffer overflow, and it was even clear what was being written into that buffer. Well, from then on, in Emscripten, fortunately, thereâs no address space randomization, and there are no holes in it, so you can write somewhere in the middle of the code outputting data from the pointer of the previous run, look at the data, check the pointer, and if it hasnât changed, you gain some insight. However, linking after any changes takes a couple of minutes, but what can you do? As a result, a specific line was found that copies BIOS from the temporary buffer to guest memory â and indeed, the buffer did not have enough space. Searching for the source of that strange buffer address led to the function qemu_anon_ram_alloc nĂ« skedarin oslib-posix.c â the logic was like this: sometimes it may be useful to align the address to a huge page of size 2 Mb, for this we will ask for mmap a little more at first, and then return the excess using munmap. And if such alignment is not required, we will instead specify the result of getpagesize() â mmap it will still give the aligned address... So in Emscripten mmap it simply calls malloc, and that, of course, does not align by page. In general, the bug that troubled me for a couple of months was fixed by a change in dy the lines.
Function calling features
And now the processor is calculating something, Qemu doesnât crash, but the screen doesnât turn on, and the processor quickly loops, judging by the output. -d exec,in_asm,out_asm. Pati Ă«shtĂ« shfaqur hipoteza: nuk po vijnĂ« ndĂ«rprerjet e timer-it (ose ndoshta asnjĂ« ndĂ«rprerje). Dhe nĂ« tĂ« vĂ«rtetĂ«, nĂ«se nga ndĂ«rtimi natyral, i cili pĂ«r njĂ« arsye punonte, hiqen ndĂ«rprerjet, nxirret njĂ« pamje e ngjashme. Por zgjidhja nuk ishte fare kĂ«tu: krahasimi i trazave tĂ« dhĂ«na me opsionin e mĂ«sipĂ«rm tregoi se trajektoret e ekzekutimit ndahen shumĂ« herĂ«t. KĂ«tu duhet thĂ«nĂ« se krahasimi i daljes sĂ« regjistruar me ndihmĂ«n e lançuesit emrun me daljen e ndĂ«rtimit natyral â nuk Ă«shtĂ« njĂ« proces krejtĂ«sisht mekanik. Nuk e di saktĂ«sisht se si programi i nisur nĂ« shfletues lidhet me emrun, por disa linja nĂ« dalje duket se janĂ« tĂ« ndĂ«rruara, kĂ«shtu qĂ« ndryshimi nĂ« diferencĂ« â nuk Ă«shtĂ« shkak pĂ«r ta konsideruar se trajektorja ndau. NĂ« pĂ«rgjithĂ«si, u bĂ« e qartĂ« se sipas udhĂ«zimeve ljmpl kalimi bĂ«het nĂ« adresa tĂ« ndryshme, dhe gjithashtu kodi bajt pĂ«rftohet nĂ« mĂ«nyrĂ« ndryshe: nĂ« njĂ« prej tyre ka njĂ« urdhĂ«r pĂ«r thirrjen e funksionit ndihmĂ«s nĂ« C, ndĂ«rsa tek tjetri â nuk ka. Pas kĂ«rkimit tĂ« udhĂ«zimit dhe studimit tĂ« kodit qĂ« kĂ«to udhĂ«zime pĂ«rkthen, u bĂ« e qartĂ« se, sĂ« pari, menjĂ«herĂ« para saj regjistri cr0 shkruhej - gjithashtu me ndihmĂ«n e njĂ« ndihmĂ«si â qĂ« shndĂ«rronte procesorin nĂ« mode tĂ« mbrojtur, dhe sĂ« dyti, qĂ« versioni js nĂ« modin e mbrojtur nuk kaloi kurrĂ«. Dhe arsyeja Ă«shtĂ« se njĂ« tjetĂ«r veçori e Emscripten Ă«shtĂ« mospranimi i kodit si implementimi i udhĂ«zimeve thirr nĂ« TCI, i cili çdo pointer nĂ« funksion e kthen nĂ« tipin long long f(int arg0, .. int arg9) â funksionet duhet tĂ« thirren me numrin e saktĂ« tĂ« argumenteve. NĂ«se kjo rregull nuk dĂ«gjohet, nĂ« varĂ«si tĂ« cilĂ«simeve tĂ« debugging-ut programi ose do tĂ« bjerĂ« (çfarĂ« Ă«shtĂ« e mirĂ«), ose do tĂ« thĂ«rrasĂ« njĂ« funksion krejtĂ«sisht tĂ« ndryshĂ«m (çfarĂ« do tĂ« ishte e vĂ«shtirĂ« pĂ«r t'u debug-uar). Ka edhe njĂ« variant tĂ« tretĂ« â tĂ« aktivizosh gjenerimin e mbĂ«shtetjeve, qĂ« shtojnĂ«/shkaktojnĂ« argumente, por gjithsej kĂ«to mbĂ«shtetje zĂ«nĂ« shumĂ« hapĂ«sirĂ«, ndĂ«rsa nĂ« fakt, mĂ« duhen vetĂ«m pak mĂ« shumĂ« se njĂ«qind mbĂ«shtetje. Vete kjo Ă«shtĂ« shumĂ« e trishtueshme, por doli njĂ« problem mĂ« i rĂ«ndĂ«sishĂ«m: nĂ« kodin e gjeneruar tĂ« funksioneve mbĂ«shtetĂ«se, argumentet u konvertuan konvertuan, por funksioni me argumentet e gjeneruara ndonjĂ«herĂ« nuk u thirr â ashtu si nĂ« implementimin tim tĂ« libffi. KĂ«shtu qĂ« disa ndihmĂ«s thjesht nuk u ekzekutuan.
Fatkeqësisht, në Qemu ka lista të lexueshme nga makina të ndihmësve në formën e një skedari titullor si
DEF_HELPER_0(lock, void)
DEF_HELPER_0(unlock, void)
DEF_HELPER_3(write_eflags, void, env, tl, i32)Ato pĂ«rdoren nĂ« njĂ« mĂ«nyrĂ« mjaft argĂ«tuese: sĂ« pari makrosat rineksionohen nĂ« njĂ« mĂ«nyrĂ« tĂ« çuditshme DEF_HELPER_n, dhe pastaj pĂ«rfshihet helper.h. Deri nĂ« atĂ« pikĂ«, makrosat zbulohet nĂ« inicializuesin e strukturĂ«s dhe njĂ« presje, pastaj pĂ«rcaktohet njĂ« varg, e nĂ« vend tĂ« elementeve â #include <helper.h> Si rezultat, mĂ« nĂ« fund u paraqit njĂ« rast pĂ«r tĂ« provuar bibliotekĂ«n , dhe u shkrua njĂ« skenar qĂ« gjeneron mbĂ«shtjellĂ«se pikĂ«risht pĂ«r ato funksione pĂ«r tĂ« cilat Ă«shtĂ« e nevojshme.
Dhe, pas kĂ«saj, procesori duket se filloi tĂ« punojĂ«. Duket se, sepse ekrani nuk u inicializua, ndonĂ«se nĂ« ndĂ«rtimin natyror arriti tĂ« niste memtest86+. Duhet tĂ« sqarojmĂ« se kodi i hyrjes bllokuese tĂ« Qemu Ă«shtĂ« shkruar me korutina. NĂ« Emscripten ka njĂ« realizim mjaft kompleks, por duhej tĂ« mbĂ«shtetej ende nĂ« kodin e Qemu, ndĂ«rsa procesori mund tĂ« debugohet tani: Qemu mbĂ«shtet opsionet -kernel, -initrd, -append, pĂ«rmes tĂ« cilave mund tĂ« ngarkohet Linux ose, pĂ«r shembull, memtest86+, pa pĂ«rdorur fare pajisjet bllokuese. Por ja çfarĂ« ndodhi: nĂ« ndĂ«rtimin natyror mund tĂ« vĂ«rehej dalja e kernel-it Linux nĂ« konsolĂ« me opsionin -nographic, ndĂ«rsa nga shfletuesi nuk kishte asnjĂ« dalje nĂ« terminal, nga ku ishte nisur emrun, nuk po vinte. Pra, nuk Ă«shtĂ« e qartĂ«: a punon procesori apo dalja e grafikĂ«s. Pastaj mĂ« erdhi nĂ« mendje tĂ« prisja pak. Doli se "procesori nuk fle, thjesht ndriçon ngadalĂ«", dhe pas pesĂ« minutash bĂ«rthama hodhi njĂ« mori mesazhesh nĂ« konsolĂ« dhe vazhdoi tĂ« ngrije. U bĂ« e qartĂ« se procesori, nĂ« pĂ«rgjithĂ«si, funksionon, dhe duhet tĂ« thellohem nĂ« kodin e punĂ«s me SDL2. TĂ« pĂ«rdor kĂ«tĂ« bibliotekĂ«, fatkeqĂ«sisht, nuk di, prandaj ndonjĂ«herĂ« duhet tĂ« veproj nĂ« mĂ«nyrĂ« tĂ« rastĂ«sishme. NĂ« njĂ« moment, nĂ« ekran u shfaq njĂ« linjĂ« parallel0 mbi njĂ« sfond blu, qĂ« nxisĂ« disa mendime. NĂ« fund doli se problemi ishte se Qemu hap disa dritare virtuale nĂ« njĂ« dritare fizike, ndĂ«rmjet tĂ« cilave mund tĂ« kalosh me Ctrl-Alt-n: nĂ« ndĂ«rtimin natyror funksionon, nĂ« Emscripten â jo. Pas eliminimit tĂ« dritareve tĂ« panevojshme me opsionet -monitor none -parallel none -serial none dhe pĂ«rcaktimit me forcĂ« pĂ«r tĂ« riparĂ« çdo ekran nĂ« çdo kornizĂ«, gjithçka papritur filloi tĂ« funksionojĂ«.
Korutina
Pra tĂ« fillojmĂ«, emulimi nĂ« shfletues funksionon, por nuk ka asgjĂ« interesante njĂ«-disk qĂ« mund tĂ« ekzekutohet aty, sepse nuk ka hyrje-dalje blloku â Ă«shtĂ« e nevojshme tĂ« implementohet mbĂ«shtetje pĂ«r korutina. NĂ« Qemu tashmĂ« ka disa backend-e korutina, por pĂ«r shkak tĂ« veçorive tĂ« JavaScript-it dhe gjeneruesit tĂ« kodit Emscripten, nuk mund tĂ« fillosh thjesht tĂ« hedhĂ«sh stack-et. Duket se "gjithçka humbi, gipsi po hiqet", por zhvilluesit e Emscripten tashmĂ« e kanĂ« pasur parasysh çdo gjĂ«. ĂshtĂ« implementuar nĂ« njĂ« mĂ«nyrĂ« mjaft interesante: le tĂ« quajmĂ« thirrjet e funksioneve tĂ« dyshimta si emscripten_sleep dhe disa tĂ« tjera qĂ« pĂ«rdorin mekanizmin Asyncify, si dhe thirrjet mbi pointer dhe thirrjet e çdo funksioni ku nĂ« stack mund tĂ« ndodhin njĂ« nga dy rastet e mĂ«parshme. Tani para çdo thirrjeje tĂ« dyshimtĂ«, do tĂ« ndajmĂ« njĂ« kontekst async, dhe menjĂ«herĂ« pas thirrjes â do tĂ« kontrollojmĂ« nĂ«se ndodhi njĂ« thirrje asinkrone, dhe nĂ«se ndodhi, do tĂ« ruajmĂ« tĂ« gjitha variablat lokale nĂ« kĂ«tĂ« kontekst async, do tĂ« tregojmĂ« se nĂ« cilin funksion duhet tĂ« kthehet kontrolli kur tĂ« nevojitet vazhdimi i ekzekutimit, dhe do ta lĂ«mĂ« funksionin aktual. KĂ«tu ka shumĂ« hapĂ«sirĂ« pĂ«r tĂ« studiuar efektin e â pĂ«r nevojat e vazhdimit tĂ« ekzekutimit tĂ« kodit pas kthimit nga njĂ« thirrje asinkrone, kompajleri gjeneron "prerje" tĂ« funksionit, qĂ« fillojnĂ« pas thirrjes sĂ« dyshimtĂ« â kĂ«shtu: nĂ«se ka n thirrje tĂ« dyshimta, funksioni do tĂ« jetĂ« i shkĂ«putur diku nĂ« n/2 herĂ« â dhe kjo Ă«shtĂ« pĂ«rveç faktit qĂ« nĂ« funksionin e origjinal duhet shtuar ruajtja e njĂ« pjese tĂ« variablave lokalĂ« pas çdo thirrjeje potencialisht asinkrone. NĂ« tĂ« ardhmen, madje mĂ« duhet tĂ« shkruaj njĂ« skenar tĂ« thjeshtĂ« nĂ« Python, i cili, pĂ«r njĂ« grup tĂ« caktuar funksionesh tĂ« veçanta, tĂ« cilat, supozimisht, "nuk lejojnĂ« asinkronĂ«sinĂ« tĂ« kalojĂ« pĂ«rmes tyre" (domethĂ«nĂ«, nĂ« to nuk ndodh rrotullimi i stack-ut dhe tĂ« gjitha ato qĂ« sapo pĂ«rshkrova), tregon se pĂ«r cilat funksione thirrjet pĂ«rmes pointer-Ă«ve duhet tĂ« injorohen nga kompajleri, kĂ«shtu qĂ« ato funksione nuk merren parasysh si asinkrone. Sepse skedarĂ«t e JS me 60 Mb â kjo Ă«shtĂ« qartĂ« shumĂ« â le tĂ« jenĂ« tĂ« paktĂ«n 30. Edhe pse, njĂ« herĂ« unĂ« po konfiguratoja njĂ« skenar ndĂ«rtimi, dhe rastĂ«sisht e hoqa opsionet e linkuesit, pĂ«rfshi kĂ«tu edhe -O3. Po çfarĂ« e kam nisur kodin e gjeneruar, Chromium konsumon memorie dhe bie. MĂ« pas, rastĂ«sisht shikova atĂ« qĂ« po pĂ«rpiqej tĂ« ngarkonte... ĂfarĂ« mund tĂ« them, do tĂ« isha i bllokuar gjithashtu po tĂ« mĂ« kĂ«rkohej tĂ« studionja dhe optimizoja JavaScript-in mbi 500 Mb.
Fatkeqësisht, verifikimet në kodin e bibliotekës që mbështet Asyncify nuk ishin aq të përputhshme me longjmp-t që përdoren në kodin e procesorit virtual, por pas një patch-i të vogël që çaktivizonte këto verifikime dhe rikthente kontekstet sikur gjithçka të ishte në rregull, kodi filloi të punonte. Dhe këtu ndodhi e çuditshme: herë pas here aktivizoheshin verifikimet në kodin e sinkronizimit - ato që e mbyllin menjëherë kodin nëse sipas logjikës së ekzekutimit duhet të bllokohet - dikush po përpiqej të kapte një mutex të kapur tashmë. Për fat, kjo nuk ishte një problem logjik në kodin e serializuar - thjesht përdora funksionalitetin standard të main loop, të ofruar nga Emscripten, por herë pas here thirrja asinkrone shkatërronte plotësisht stekën, dhe në atë moment aktivizohej setTimeout nga main loop - kështu që, kodi hynte në iteracionin e ciklit kryesor, pa dalë nga iteracioni i mëparshëm. E rishkrova në një cikël të pafund dhe emscripten_sleep, dhe problemet me mutex-t pushuan. Kodi u bë edhe më logjik - sepse, në thelb, nuk kam një kod që përgatit një kornizë të re animacioni - thjesht procesori diçka llogarit dhe ekrani përditësohet periodikisht. Megjithatë, problemet nuk përfunduan këtu: herë pas here ekzekutimi i Qemu thjesht përfundonte heshtazi pa ndonjë lloj përjashtimi dhe gabimi. Në atë moment nuk e kushtova vëmendje, por, për të mos kaluar përpara, do të them se problemi ishte ky: kodi i korutinave, në të vërtetë, nuk përdor setTimeout (ose, të paktën, jo aq shpesh sa mund të mendohet): funksioni emscripten_yield thjesht vendos një flamur thirrjeje asinkrone. E gjithë thelbi është se emscripten_coroutine_next nuk është një funksion asinkron: brenda vetes kontrollon flamurin, e fshin atë dhe kalon kontrollin ku duhet. Pra, në të ndalet zhvillimi i stekës. Problemi ishte se për shkak të use-after-free, i cili shfaqej kur ishte e çaktivizuar grupa e korutinave për shkak se nuk kopjova një rresht të rëndësishëm të kodit nga backend-i ekzistues i korutinave, funksioni qemu_in_coroutine kthehej true, kur në realitet duhet të kishte kthyer false. Kjo çonte në thirrje emscripten_yield, mbi të cilën lart nuk kishte stek emscripten_coroutine_next, steku u shtrir deri në majë, por asnjë setTimeout, siç thashë, nuk ishte e vendosur.
Kodin e gjenerimit JavaScript
Dhe ja, pikĂ«risht ajo "pĂ«rvijim e mishit prapa" qĂ« premtohej. NĂ« tĂ« vĂ«rtetĂ« jo. Sigurisht, nĂ«se ekzekutojmĂ« Qemu nĂ« shfletues, dhe brenda tij â Node.js, atĂ«herĂ«, natyrisht, pas gjenerimit tĂ« kodit nĂ« Qemu do tĂ« marrim njĂ« JavaScript krejt ndryshe. Por mĂ« çdo rast, ndonjĂ« kthim mbrapa.
NĂ« fillim pak pĂ«r funksionimin e Qemu. MenjĂ«herĂ« ju lutem falni: unĂ« nuk jam njĂ« zhvillues profesionist i Qemu dhe pĂ«rfundimet e mia mund tĂ« jenĂ« disa herĂ« tĂ« gabuara. Siç thonĂ«, "opinionet e studentĂ«ve nuk janĂ« tĂ« detyuara tĂ« pĂ«rputhen me mendimet e profesorĂ«ve, aksiomat e Peano dhe shĂ«ndoshĂ«sinĂ« e mendjes". Qemu ka njĂ« numĂ«r arkitekturash mikse qĂ« mbĂ«shteten dhe pĂ«r çdo njĂ«ri ka njĂ« katalog si target-i386. GjatĂ« ndĂ«rtimit mund tĂ« specifikoni mbĂ«shtetje pĂ«r disa arkitektura mikse, por rezultati do tĂ« jetĂ« thjesht disa binarĂ«. Kodi pĂ«r mbĂ«shtetje tĂ« arkitekturĂ«s mikse, nga ana e tij, gjeneron disa operacione tĂ« brendshme tĂ« Qemu, tĂ« cilat TCG (Tiny Code Generator) tashmĂ« i kthen nĂ« kodin makinerik tĂ« arkitekturĂ«s sĂ« hostit. Siç thotĂ« nĂ« skedarin readme qĂ« ndodhet nĂ« katalogun e tcg, fillimisht zbatohej si pjesĂ« e njĂ« kompilerit standard C, e cila mĂ« pas u pĂ«rshtat pĂ«r JIT. Prandaj, pĂ«r shembull, arkitektura e synuar nĂ« terma tĂ« kĂ«tij dokumenti â nuk Ă«shtĂ« mĂ« arkitektura mikse, por arkitektura e hostit. NĂ« njĂ« pikĂ«, u shfaq njĂ« komponent tjetĂ«r â Tiny Code Interpreter (TCI), i cili duhet tĂ« ekzekutojĂ« kodin (praktikisht ato operacione tĂ« brendshme) nĂ« mungesĂ« tĂ« njĂ« gjeneruesi kodi pĂ«r arkitekturĂ«n e caktuar tĂ« hostit. NĂ« tĂ« vĂ«rtetĂ«, siç thuhet nĂ« dokumentacionin e tij, ky interpretuese mund tĂ« mos funksionojĂ« gjithmonĂ« aq mirĂ« sa gjeneruesi i kodit JIT jo vetĂ«m nĂ« aspektin numerik tĂ« shpejtĂ«sisĂ«, por edhe tĂ« cilĂ«sisĂ«. MegjithatĂ« nuk jam i sigurt se pĂ«rshkrimi i tij Ă«shtĂ« plotĂ«sisht aktual.
Në fillim përpiqesha të bëja një backend të plotë TCG, por shpejt u konfuzova në kodet burimore dhe përshkrimin e paqartë të udhëzimeve të kodit të byte, kështu që vendosa ta mbështes interpretuesin TCI. Kjo solli disa përfitime:
- në implementimin e gjeneruesit të kodit mund të shihja jo përshkrimin e udhëzimeve, por kodin e interpretuesit
- mund të krijoni funksione jo për çdo bllok të takimit, por për shembull, vetëm pas ekzekutimit të njëqind
- në rast të ndryshimit të kodit të gjeneruar (dhe duket se kjo është e mundur, duke parë funksionet me emra që përmbajnë fjalën patch) do të duhet ta invalidizoj kodin JS të gjeneruar, por të paktën do të kem nga të cilat ta rregullosh përsëri
Sa për pikën e tretë, nuk jam i sigurt nëse patchimi është i mundur pas herës së parë që kodek ekzekutohet, por dy pikave të para mjaftojnë.
Fillimisht, kodi u gjenerua si njĂ« switch i madh nĂ« adresĂ«n e instruksionit fillestar tĂ« bytecode, por mĂ« pas, duke kujtuar njĂ« artikull nĂ« lidhje me Emscripten, optimizimin e JS tĂ« gjeneruar dhe rilooping, vendosa tĂ« gjeneroja kod mĂ« tĂ« lexueshĂ«m, veçanĂ«risht duke e parĂ« qĂ« empirike rezultonte se pika e vetme e hyrjes nĂ« bllokun e takimit Ă«shtĂ« fillimi i tij. U tha â u bĂ«, pas pak kohĂ«sh doli njĂ« gjenerator kodi, qĂ« gjeneron kode me if-e (edhe pse pa cikle). Por ja, problemi, ai binte, duke dhĂ«nĂ« njĂ« mesazh se instruksioni u shfaq si njĂ« gjatĂ«si e gabuar. NĂ« kĂ«tĂ« rast, instruksioni i fundit nĂ« kĂ«tĂ« nivel rekursioni ishte brcond. MirĂ«, do tĂ« shtoj njĂ« kontroll identik nĂ« gjenerimin e kĂ«tij instruksioni para thirrjes rekursive dhe pas, dhe⊠asnjĂ« nga to nuk u ekzekutua, por pas switch-it mbi assert-it ne tĂ« gjithĂ« bĂ«mĂ« rĂ«nie. NĂ« fund tĂ« fundit, duke studiuar kodin e gjeneruar, kuptova se pas switch-it, treguesi i instruksionit aktual rikthehet nga stack-u dhe, pĂ«r sa duket, mbulohet nga kodi JavaScript tĂ« gjeneruar. KĂ«shtu ndodhi. Rritja e tamponit nga njĂ« megabajt nĂ« dhjetĂ« nuk solli asgjĂ«, dhe u bĂ« e qartĂ« se gjeneratori i kodit po pĂ«rfundonte nĂ« njĂ« cikĂ«l. Duhet tĂ« kontrolloja nĂ«se nuk kemi dalĂ« nga kufijtĂ« e TB aktuale dhe nĂ«se dolĂ«m, tĂ« jap adresĂ«n e TB-tĂ« tjetĂ«r me shenjĂ« negative, nĂ« mĂ«nyrĂ« qĂ« tĂ« mund tĂ« vazhdojmĂ« ekzekutimin. PĂ«r mĂ« tepĂ«r, kjo zgjidh problemin "cila funksion e gjeneruar tĂ« invalidizohet nĂ«se ky copĂ« bytecode ndryshon?" â duhet tĂ« invalidizohet vetĂ«m funksioni qĂ« i pĂ«rket kĂ«tij blloku tĂ« takimit. PĂ«rveç kĂ«saj, ndonĂ«se e gjitha e testova nĂ« Chromium (pasi po pĂ«rdor Firefox dhe mĂ« Ă«shtĂ« mĂ« lehtĂ« tĂ« pĂ«rdor njĂ« shfletues tĂ« veçantĂ« pĂ«r eksperimente), por Firefox mĂ« ndihmoi tĂ« rregulloj mos pĂ«rputhjet me standardin asm.js, pas sĂ« cilĂ«s kodi filloi tĂ« punonte mĂ« shpejt nĂ« Chromun.
Shembuj i kodit të gjeneruar
Compiling 0x15b46d0:
CompiledTB[0x015b46d0] = function(stdlib, ffi, heap) {
"use asm";
var HEAP8 = new stdlib.Int8Array(heap);
var HEAP16 = new stdlib.Int16Array(heap);
var HEAP32 = new stdlib.Int32Array(heap);
var HEAPU8 = new stdlib.Uint8Array(heap);
var HEAPU16 = new stdlib.Uint16Array(heap);
var HEAPU32 = new stdlib.Uint32Array(heap);
var dynCall_iiiiiiiiiii = ffi.dynCall_iiiiiiiiiii;
var getTempRet0 = ffi.getTempRet0;
var badAlignment = ffi.badAlignment;
var _i64Add = ffi._i64Add;
var _i64Subtract = ffi._i64Subtract;
var Math_imul = ffi.Math_imul;
var _mul_unsigned_long_long = ffi._mul_unsigned_long_long;
var execute_if_compiled = ffi.execute_if_compiled;
var getThrew = ffi.getThrew;
var abort = ffi.abort;
var qemu_ld_ub = ffi.qemu_ld_ub;
var qemu_ld_leuw = ffi.qemu_ld_leuw;
var qemu_ld_leul = ffi.qemu_ld_leul;
var qemu_ld_beuw = ffi.qemu_ld_beuw;
var qemu_ld_beul = ffi.qemu_ld_beul;
var qemu_ld_beq = ffi.qemu_ld_beq;
var qemu_ld_leq = ffi.qemu_ld_leq;
var qemu_st_b = ffi.qemu_st_b;
var qemu_st_lew = ffi.qemu_st_lew;
var qemu_st_lel = ffi.qemu_st_lel;
var qemu_st_bew = ffi.qemu_st_bew;
var qemu_st_bel = ffi.qemu_st_bel;
var qemu_st_leq = ffi.qemu_st_leq;
var qemu_st_beq = ffi.qemu_st_beq;
function tb_fun(tb_ptr, env, sp_value, depth) {
tb_ptr = tb_ptr|0;
env = env|0;
sp_value = sp_value|0;
depth = depth|0;
var u0 = 0, u1 = 0, u2 = 0, u3 = 0, result = 0;
var r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0;
var r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0, r16 = 0, r17 = 0, r18 = 0, r19 = 0;
var r20 = 0, r21 = 0, r22 = 0, r23 = 0, r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0;
var r30 = 0, r31 = 0, r41 = 0, r42 = 0, r43 = 0, r44 = 0;
r14 = env|0;
r15 = sp_value|0;
START: do {
r0 = HEAPU32[((r14 + (-4))|0) >> 2] | 0;
r42 = 0;
result = ((r0|0) != (r42|0))|0;
HEAPU32[1445307] = r0;
HEAPU32[1445321] = r14;
if(result|0) {
HEAPU32[1445322] = r15;
return 0x0345bf93|0;
}
r0 = HEAPU32[((r14 + (16))|0) >> 2] | 0;
r42 = 8;
r0 = ((r0|0) - (r42|0))|0;
HEAPU32[(r14 + (16)) >> 2] = r0;
r1 = 8;
HEAPU32[(r14 + (44)) >> 2] = r1;
r1 = r0|0;
HEAPU32[(r14 + (40)) >> 2] = r1;
r42 = 4;
r0 = ((r0|0) + (r42|0))|0;
r2 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
HEAPU32[1445307] = r0;
HEAPU32[1445308] = r1;
HEAPU32[1445309] = r2;
HEAPU32[1445321] = r14;
HEAPU32[1445322] = r15;
qemu_st_lel(env|0, r0|0, r2|0, 34, 22759218);
if(getThrew() | 0) abort();
r0 = 3241038392;
HEAPU32[1445307] = r0;
r0 = qemu_ld_leul(env|0, r0|0, 34, 22759233)|0;
if(getThrew() | 0) abort();
HEAPU32[(r14 + (24)) >> 2] = r0;
r1 = HEAPU32[((r14 + (12))|0) >> 2] | 0;
r2 = HEAPU32[((r14 + (40))|0) >> 2] | 0;
HEAPU32[1445307] = r0;
HEAPU32[1445308] = r1;
HEAPU32[1445309] = r2;
qemu_st_lel(env|0, r2|0, r1|0, 34, 22759265);
if(getThrew() | 0) abort();
r0 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
HEAPU32[(r14 + (40)) >> 2] = r0;
r1 = 24;
HEAPU32[(r14 + (52)) >> 2] = r1;
r42 = 0;
result = ((r0|0) == (r42|0))|0;
if(result|0) {
HEAPU32[1445307] = r0;
HEAPU32[1445308] = r1;
}
HEAPU32[1445307] = r0;
HEAPU32[1445308] = r1;
return execute_if_compiled(22759392|0, env|0, sp_value|0, depth|0) | 0;
return execute_if_compiled(23164080|0, env|0, sp_value|0, depth|0) | 0;
break;
} while(1); abort(); return 0|0;
}
return {tb_fun: tb_fun};
}(window, CompilerFFI, Module.buffer)["tb_fun"]Përfundim
Pra, puna ende nuk është përfunduar, por m'u bë e mërzitshme ta çoj më tej këtë projekt të gjatë në fshehtësi. Prandaj vendosa të publikoj për momentin atë që kam. Kodi është paksa i frikshëm në disa vende, sepse është një eksperiment, dhe nuk është e qartë paraprakisht se çfarë duhet bërë. Ndoshta, më vonë do të duhet të bëj komitete normal, atomike mbi ndonjë version më të modern të Qemu. Për momentin, ka një degë në git në formatin e blogut: për secilën "nivel" të kaluar siç duhet, është shtuar një koment i zgjeruar në gjuhën ruse. Në thelb, ky artikull është në një masë të konsiderueshme një përmbledhje e rezultateve git log.
Mund ta provoni këtë të gjithë (kini kujdes, trafik).
ĂfarĂ« funksionon tashmĂ«:
- Funksionon procesori virtual x86
- Ka një prototip funksional të gjeneratorit të kodit JIT nga kodi makinerik në JavaScript
- Ekziston një skicë për ndërtimin e arkitekturave të tjera 32-bitësh të mysafirëve: ju mund të admironit tani në shfletuesin, ndërsa ngarkohet Linux për arkitekturën MIPS
ĂfarĂ« mund tĂ« bĂ«het tjetĂ«r
- Të përshpejtohet emulimi. Edhe në modin JIT, duket se ai punon më ngadalë se Virtual x86 (për fat të keq, ka një Qemu me shumë hardware dhe arkitektura që emulon)
- Të bëj një ndërfaqe normale - si zhvillues web-i, të themi, nuk jam shumë i aftë, prandaj deri tani e kam ribërë standardin e shell-it Emscripten, sa kam mundur
- Të provojë të nisë funksione më të komplikuara të Qemu - rrjeti, migrimi i VM etj.
- UPD: do të duhet të dërgoj në upstream Emscripten disa nga punimet e mia të pakta dhe raportet e defekteve, siç kanë bërë portuesit e mëparshëm të Qemu dhe projekteve të tjera. Falë tyre që kishte mundësi të përdorja në heshtje kontributin e tyre në Emscripten në kuadër të detyrës sime.
Burimi: habr.com
