QEMU.js: avà seriu è cù WASM

Una volta aghju decisu per piacè prova a reversibilità di u prucessu è amparà cumu generà JavaScript (più precisamente, Asm.js) da u codice macchina. QEMU hè statu sceltu per l'esperimentu, è qualchì tempu dopu un articulu hè statu scrittu annantu à Habr. In i cumenti, mi sò cunsigliatu di rinfurzà u prugettu in WebAssembly, è ancu abbandunà quasi finitu In qualchì modu ùn vulia micca u prughjettu ... U travagliu era andatu, ma assai pianu pianu, è avà, pocu tempu in quellu articulu apparsu cummentariu nantu à u tema "Allora cumu hè finitu tuttu?" In risposta à a mo risposta dettagliata, aghju intesu "Questu sona cum'è un articulu". Ebbè, se pudete, ci sarà un articulu. Forse qualcunu truverà utile. Da ellu, u lettore ampararà qualchi fatti nantu à u disignu di i backend di generazione di codice QEMU, è ancu cumu scrive un compilatore Just-in-Time per una applicazione web.

fatti

Siccomu avia digià amparatu à "in qualchì manera" portà QEMU à JavaScript, sta volta hè statu decisu di fà cun prudenza è micca ripetiri vechji sbagli.

Errore numeru unu: branch from point release

U mo primu sbagliu hè statu di fork a mo versione da a versione upstream 2.4.1. Allora mi pareva una bona idea: s'ellu ci hè una liberazione puntuale, allora hè probabilmente più stabile chì a simplicità 2.4, è ancu più u ramu. master. E postu ch'e aghju pensatu à aghjunghje una bona quantità di i mo propri bugs, ùn aghju micca bisognu di nimu d'altru. Hè prubabilmente cum'è hè risultatu. Ma quì hè a cosa: QEMU ùn si ferma, è in un certu puntu anu ancu annunziatu l'ottimisazione di u codice generatu da u percentu 10. "Iè, avà aghju da congelate", aghju pensatu è rottu. Quì avemu bisognu di fà una digressione: per via di a natura di un filu unicu di QEMU.js è u fattu chì u QEMU originale ùn implica micca l'absenza di multi-threading (vale à dì, a capacità di uperà simultaneamente parechji camini di codice senza relazione, è micca solu "usà tutti i kernels") hè criticu per questu, e funzioni principali di i filamenti aghju avutu "turn it out" per pudè chjamà da fora. Questu hà creatu qualchi prublemi naturali durante a fusione. Tuttavia, u fattu chì alcuni di i cambiamenti da u ramu master, cù quale aghju pruvatu à unisce u mo codice, sò stati ancu ciliegie scelti in u puntu di liberazione (è dunque in u mo ramu) ancu prubabilmente ùn avissi micca aghjustatu comodità.

In generale, aghju decisu chì hè sempre sensu per scaccià u prototipu, smontallu per i pezzi è custruisce una nova versione da zero basatu annantu à qualcosa più frescu è avà da master.

Errore numeru dui: metodulugia TLP

In essenza, questu ùn hè micca un sbagliu, in generale, hè solu una caratteristica di creà un prughjettu in cundizioni di malintesi cumpleta di "induve è cumu si move?" è in generale "avemu da ghjunghje?" In sti cundizioni prugrammazione goffa era una opzione ghjustificata, ma, naturalmente, ùn vulia micca ripetiri inutilmente. Sta volta aghju vulsutu fà sàviu: impegni atomichi, cambiamenti di codice cuscenti (è micca "stringing characters random together until it compiles (with warnings)"), cum'è Linus Torvalds hà dettu una volta di qualchissia, secondu Wikiquote), etc.

Errore numeru trè: mette in l'acqua senza cunnosce u gudu

Ùn aghju micca sguassatu cumplettamente di questu, ma avà aghju decisu di ùn seguità a strada di a minima resistenza, è di fà u "modu adultu", vale à dì, scrivite u mo backend TCG da zero, per ùn avè micca. per avè da dì dopu, "Iè, questu hè sicuru, pianu pianu, ma ùn possu micca cuntrullà tuttu - hè cusì chì TCI hè scrittu ..." Inoltre, sta inizialmente pareva una suluzione ovvia, postu chì Aghju generatu codice binariu. Cum'è dicenu, "Ghent hà riunituу, ma micca quellu ": u codice hè, sicuru, binariu, ma u cuntrollu ùn pò micca esse simpliciamente trasferitu à questu - deve esse esplicitamente imbuttatu in u navigatore per a compilazione, risultatu in un certu oggettu da u mondu JS, chì deve ancu esse. esse salvatu in qualchì locu. In ogni casu, nantu à l'architetture RISC normale, finu à chì aghju capitu, una situazione tipica hè a necessità di resettate esplicitamente a cache d'istruzzioni per u codice regeneratu - se questu ùn hè micca ciò chì avemu bisognu, allora, in ogni casu, hè vicinu. Inoltre, da u mo ultimu tentativu, aghju amparatu chì u cuntrollu ùn pare micca esse trasferitu à a mità di u bloccu di traduzzione, cusì ùn avemu micca veramente bisognu di bytecode interpretatu da qualsiasi offset, è pudemu simpricimenti generà da a funzione nantu à TB. .

Sò ghjunti è anu calciatu

Ancu se aghju cuminciatu à riscrivà u codice in u lugliu, un puzzicheghju magicu hè ghjuntu inosservatu: di solitu e lettere da GitHub arrivanu cum'è notifiche nantu à risposti à Issues and Pull requests, ma quì, di colpu menzione in u filu Binaryen cum'è un backend qemu in u cuntestu, "Hà fattu qualcosa cusì, forse dirà qualcosa". Parlavamu di l'usu di a biblioteca ligata di Emscripten Binaryen per creà WASM JIT. Ebbè, aghju dettu chì avete una licenza Apache 2.0 quì, è QEMU in tuttu hè distribuitu sottu GPLv2, è ùn sò micca assai cumpatibili. Di colpu si girò fora chì una licenza pò esse ripara in una certa manera (Ùn sò micca: forse cambià, forse doppia licenza, forse qualcos'altro...). Questu, sicuru, m'hà fattu cuntentu, perchè à quellu tempu aghju digià guardatu attentamente formatu binariu WebAssembly, è eru in qualchì manera triste è incomprensibile. Ci era ancu una biblioteca chì devore i blocchi basi cù u gràficu di transizione, pruduce u bytecode, è ancu eseguisce in l'interprete stessu, se ne necessariu.

Allora ci era più una lettera nantu à a lista di mailing QEMU, ma questu hè più nantu à a quistione, "Quale hà bisognu in ogni modu?" È hè di colpu, resultò chì era necessariu. À u minimu, pudete scaccià e seguenti pussibulità di usu, se travaglia più o menu rapidamente:

  • lanciari qualcosa educativu senza alcuna installazione à tutti
  • virtualizazione in iOS, induve, sicondu i rumuri, l'unica applicazione chì hà u dirittu di generazione di codice in u volu hè un mutore JS (hè veru?)
  • dimostrazione di mini-OS - single-floppy, built-in, tutti i tipi di firmware, ecc...

Funzioni di runtime di u navigatore

Cumu l'aghju digià dettu, QEMU hè ligatu à multithreading, ma u navigatore ùn hà micca. Ebbè, questu hè, micca ... À u primu ùn esiste micca in tuttu, dopu WebWorkers apparsu - per quantu aghju capitu, questu hè multithreading basatu nantu à u passaghju di messagiu. senza variabili spartuti. Naturalmente, questu crea prublemi significativi quandu si portà u codice esistente basatu annantu à u mudellu di memoria cumuni. Allora, sottu a pressione publica, hè stata implementata sottu u nome SharedArrayBuffers. Hè statu introduttu à pocu à pocu, celebravanu u so lanciu in diversi navigatori, dopu celebravanu l'annu novu, è dopu Meltdown ... Dopu à quale sò ghjunti à a cunclusione chì grossu o grossu a misurazione di u tempu, ma cù l'aiutu di a memoria sparta è un thread incrementing the counter, hè tuttu u listessu funzionerà abbastanza precisamente. Cusì avemu disattivatu multithreading cù memoria spartuta. Sembra ch'elli l'anu turnatu dopu, ma, cum'è hè diventatu chjaru da u primu esperimentu, ci hè una vita senza ellu, è s'ellu hè cusì, avemu da pruvà à fà senza invià à multithreading.

A seconda funzione hè l'impossibilità di manipulazioni di livellu bassu cù a pila: ùn pudete micca solu piglià, salvà u cuntestu attuale è cambià à una nova cù una nova pila. A pila di chjama hè gestita da a macchina virtuale JS. Sembra, chì hè u prublema, postu chì avemu sempre decisu di gestisce l'antichi flussi completamente manualmente? U fattu hè chì u bloccu I / O in QEMU hè implementatu attraversu coroutines, è questu hè quì chì e manipulazioni di stack di livellu bassu seranu utile. Fortunatamente, Emscipten cuntene digià un mecanismu per operazioni asincrone, ancu dui: Asyncify и Interprete. U primu travaglia per un bloat significativu in u codice JavaScript generatu è ùn hè più supportatu. U sicondu hè u "modu currettu" attuale è travaglia per a generazione di bytecode per l'interprete nativu. Funziona, sicuru, pianu pianu, ma ùn gonfia micca u codice. Hè veru, u supportu per i coroutines per stu mecanismu duvia esse cuntribuitu indipindentamente (ci era digià coroutines scritte per Asyncify è ci era una implementazione di apprussimatamente a stessa API per Emterpreter, avete solu bisognu di cunnette).

À u mumentu, ùn aghju micca ancu riesciutu à sparte u codice in un cumpilatu in WASM è interpretatu cù Emterpreter, cusì i dispositi di bloccu ùn anu micca travagliatu ancu (vede in a prossima serie, cum'è dicenu...). Questu hè, à a fine, duvete ottene qualcosa cum'è questa cosa divertente in strati:

  • I/O di bloccu interpretatu. Ebbè, avete veramente aspittatu NVMe emulatu cù prestazioni native? 🙂
  • codice QEMU principale compilatu staticamente (traduttore, altri dispositi emulati, etc.)
  • codice d'ospiti cumpilatu dinamicamente in WASM

Caratteristiche di e fonti QEMU

Cum'è probabilmente avete digià capitu, u codice per emulà l'architetture di l'ospiti è u codice per a generazione di l'istruzzioni di a macchina host sò separati in QEMU. In fatti, hè ancu un pocu più complicatu:

  • ci sò architetture d'ospiti
  • acceleratori, vale à dì, KVM per a virtualizazione di hardware in Linux (per i sistemi ospiti è ospiti cumpatibili cù l'altri), TCG per a generazione di codice JIT in ogni locu. Partendu da QEMU 2.9, hè apparsu u supportu per u standard di virtualizazione hardware HAXM in Windows (i dettagli)
  • se TCG hè utilizatu è micca a virtualizazione hardware, allora hà un supportu di generazione di codice separatu per ogni architettura d'ospiti, è ancu per l'interprete universale.
  • ... è intornu à tuttu questu - periferiche emulate, interfaccia d'utilizatore, migrazione, record-replay, etc.

Per via, sapete: QEMU pò emulà micca solu l'urdinatore sanu, ma ancu u processatore per un prucessu d'utilizatore separatu in u kernel host, chì hè utilizatu, per esempiu, da l'AFL fuzzer per l'instrumentazione binaria. Forse qualchissia vulete portà stu modu di operazione di QEMU à JS? 😉

Cum'è a maiò parte di u software liberu di longu tempu, QEMU hè custruitu attraversu a chjama configure и make. Diciamu chì decide di aghjunghje qualcosa: un backend TCG, implementazione di filu, qualcosa altru. Ùn affrettate micca à esse felice / orrificatu (sottolineate cum'è apprupriatu) à a prospettiva di cumunicà cù Autoconf - in fattu, configure QEMU hè apparentemente scritta da sè stessu è ùn hè micca generata da nunda.

assemblea web

Allora chì hè questa cosa chjamata WebAssembly (aka WASM)? Questu hè un rimpiazzamentu per Asm.js, ùn pretende più esse un codice JavaScript validu. À u cuntrariu, hè puramente binariu è ottimizatu, è ancu simpliciamente scrivendu un interu in questu ùn hè micca assai simplice: per a compattezza, hè guardatu in u formatu. LEB128.

Puderete avè intesu parlà di l'algoritmu di relooping per Asm.js - questu hè a risturazione di struzzioni di cuntrollu di flussu "altu livellu" (vale à dì, se-then-else, loops, etc.), per quale i motori JS sò cuncepiti, da u LLVM IR di livellu bassu, più vicinu à u codice di a macchina eseguitu da u processatore. Naturalmente, a rapprisentazione intermedia di QEMU hè più vicinu à a seconda. Sembra chì quì hè quì, bytecode, a fine di u turmentu... È po ci sò i blocchi, si-e-poi-altri è i loops !..

È questu hè un altru mutivu perchè Binaryen hè utile: pò accettà naturalmente blocchi d'altu livellu vicinu à ciò chì seria almacenatu in WASM. Ma pò ancu pruduce codice da un gràficu di blocchi basi è transizioni trà elli. Ebbè, aghju digià dettu chì oculta u formatu di almacenamiento WebAssembly daretu à l'API C/C++ còmuda.

TCG (Tiny Code Generator)

GTC era originariamente backend per u compilatore C. Allora, apparentemente, ùn pudia micca sustene a cumpetizione cù GCC, ma à a fine hà truvatu u so postu in QEMU cum'è un mecanismu di generazione di codice per a piattaforma host. Ci hè ancu un backend TCG chì genera un bytecode astrattu, chì hè immediatamente eseguitu da l'interprete, ma aghju decisu di evità di utilizà sta volta. Tuttavia, u fattu chì in QEMU hè digià pussibule di attivà a transizione à a TB generata attraversu a funzione tcg_qemu_tb_exec, resultò esse assai utile per mè.

Per aghjunghje un novu backend TCG à QEMU, avete bisognu di creà un subdirectory tcg/<имя архитектуры> (in stu casu, tcg/binaryen), è cuntene dui schedari: tcg-target.h и tcg-target.inc.c и prescrive hè tuttu circa configure. Pudete mette altri fugliali quì, ma, cum'è pudete guessà da i nomi di sti dui, i dui seranu inclusi in qualchì locu: unu cum'è un schedariu di header regulare (hè inclusu in tcg/tcg.h, è quellu hè digià in altri schedari in i cartulari tcg, accel è micca solu), l'altru - solu cum'è un snippet di codice in tcg/tcg.c, ma hà accessu à e so funzioni statiche.

Decidendu chì passassi troppu tempu nantu à investigazioni dettagliate di u funziunamentu, aghju simplicemente copiatu i "scheletri" di sti dui schedari da un'altra implementazione backend, onestamente indicà questu in l'intestazione di licenza.

u schedariu tcg-target.h cuntene principalmente paràmetri in a forma #define-s:

  • quanti registri è chì larghezza ci sò nantu à l'architettura di destinazione (avemu quanti ne vulemu, quanti vulemu - a quistione hè più nantu à ciò chì serà generatu in codice più efficau da u navigatore nantu à l'architettura "completamente target") ...)
  • allinamentu di l'istruzzioni di l'ospiti: nantu à x86, è ancu in TCI, l'istruzzioni ùn sò micca allinati in tuttu, ma aghju da mette in u buffer di codice micca struzzioni à tutti, ma punters à strutture di biblioteca Binaryen, cusì diceraghju: 4 bytes
  • quali struzzioni opzionali u backend pò generà - includemu tuttu ciò chì truvamu in Binaryen, lasciate chì l'acceleratore rompe u restu in più simplici stessu
  • Chì ghjè a dimensione apprussimata di a cache TLB dumandata da u backend. U fattu hè chì in QEMU tuttu hè seriu: ancu s'ellu ci sò funzioni d'aiutu chì realizanu a carica / magazzinu in cunsiderà u MMU d'ospiti (induve sariamu senza avà?), salvanu a so cache di traduzzione in forma di una struttura, u Trattamentu di quale hè convenientu per incrustà direttamente in blocchi di trasmissione. A quistione hè, chì offset in questa struttura hè processatu in modu più efficace da una sequenza chjuca è rapida di cumandamenti?
  • quì pudete aghjustà u scopu di unu o dui registri riservati, attivà chjamà TB attraversu una funzione è, eventualmente, descrive un paru di picculi inline-funzioni cum'è flush_icache_range (ma questu ùn hè micca u nostru casu)

u schedariu tcg-target.inc.c, sicuru, hè di solitu assai più grande in grandezza è cuntene parechje funzioni obligatorii:

  • inizializazione, cumprese restrizioni nantu à quali struzzioni ponu operare nantu à quale operandi. Copiatu sfarente da mè da un altru backend
  • funzione chì piglia una struzzione interna di bytecode
  • Pudete ancu mette funzioni ausiliarii quì, è pudete puru aduprà funzioni statiche da tcg/tcg.c

Per mè stessu, aghju sceltu a seguente strategia: in e prime parolle di u prossimu bloccu di traduzzione, aghju scrittu quattru punte: una marca di partenza (un certu valore in a vicinanza). 0xFFFFFFFF, chì hà determinatu u statu attuale di a TB), u cuntestu, u modulu generatu è u numeru magicu per a debugging. À u principiu, a marca hè stata postu 0xFFFFFFFF - ninduve n - un picculu numeru pusitivu, è ogni volta chì hè stata eseguita attraversu l'interprete hà aumentatu da 1. Quandu hà righjuntu 0xFFFFFFFE, a compilazione hè stata fatta, u modulu hè statu salvatu in a tavola di funzioni, impurtatu in un picculu "launcher", in quale l'esekzione passava da tcg_qemu_tb_exec, è u modulu hè statu eliminatu da a memoria QEMU.

Per parafrasà i classici, "Crutch, quantu hè intrecciatu in stu sonu per u core di u proger...". Tuttavia, a memoria fugliava in qualchì locu. In più, era memoria gestita da QEMU! Aviu avutu un codice chì, quandu scrivite l'istruzzioni dopu (bene, vale à dì, un puntatore), sguassate quellu chì u ligame era in questu locu prima, ma questu ùn hà micca aiutu. In realtà, in u casu più simplice, QEMU allocate memoria à l'iniziu è scrive u codice generatu quì. Quandu u buffer scorri, u codice hè ghjittatu fora è u prossimu principia à esse scrittu in u so locu.

Dopu avè studiatu u codice, aghju capitu chì u truccu cù u numeru magicu m'hà permessu di ùn fallu micca in a distruzzione di u munzeddu, liberendu qualcosa di sbagliatu nantu à un buffer micca inizializatu in u primu passu. Ma quale riscrive u buffer per aggira a mo funzione dopu? Cum'è i sviluppatori di Emscripten cunsiglianu, quandu aghju avutu un prublema, aghju purtatu u codice resultanti torna à l'applicazione nativa, aghjustate Mozilla Record-Replay nantu à questu ... In generale, à a fine aghju realizatu una cosa simplice: per ogni bloccu, a struct TranslationBlock cù a so descrizzione. Indovinate induve... Hè ghjustu, ghjustu prima di u bloccu ghjustu in u buffer. Capendu questu, aghju decisu di abbandunà cù crutches (almenu alcune), è simpricimenti scacciò u numeru magicu, è trasfirìu e parolle restante à struct TranslationBlock, creendu una lista ligata singola chì pò esse rapidamente attraversata quandu a cache di traduzzione hè resettata, è libera a memoria.

Certi crutches restanu: per esempiu, punters marcati in u buffer di codice - alcuni di elli sò simpliciamente BinaryenExpressionRef, vale à dì, fighjanu à l'espressioni chì deve esse linealemente mette in u bloccu di basa generatu, a parte hè a cundizione per a transizione trà BB, a parte hè induve andà. Ebbè, ci sò digià blocchi preparati per Relooper chì deve esse cunnessi secondu e cundizioni. Per distinguelli, l'assunzione hè aduprata chì sò tutti allinati da almenu quattru byte, perchè pudete aduprà in modu sicuru u minimu significativu di dui bits per l'etichetta, avete bisognu di ricurdà di sguassà se ne necessariu. In modu, tali etichette sò digià aduprate in QEMU per indicà u mutivu di esce da u ciclu TCG.

Utilizà Binaryen

I moduli in WebAssembly cuntenenu funzioni, ognuna di quale cuntene un corpu, chì hè una espressione. L'espressioni sò operazioni unarie è binari, blocchi custituiti da liste di altre espressioni, flussu di cuntrollu, etc. Comu aghju digià dettu, u flussu di cuntrollu quì hè urganizatu precisamente cum'è rami d'altu livellu, loops, chjama di funzione, etc. L'argumenti à e funzioni ùn sò micca passati nantu à a pila, ma esplicitamente, cum'è in JS. Ci sò ancu variàbili glubale, ma ùn l'aghju micca utilizatu, perchè ùn vi ne dicu micca.

E funzioni anu ancu variabili lucali, numerati da zero, di tipu: int32 / int64 / float / double. In questu casu, i primi n variàbili lucali sò l'argumenti passati à a funzione. Per piacè nutate chì, ancu s'è tuttu quì ùn hè micca cumplettamente di livellu bassu in quantu à u flussu di cuntrollu, i numeri interi ùn portanu micca l'attributu "firmatu / senza firmatu": cumu si cumporta u numeru dipende da u codice di l'operazione.

In generale, Binaryen furnisce semplice C-API: create un modulu, in ellu creà espressioni - unaria, binari, blocchi da altre espressioni, flussu di cuntrollu, etc. Allora crea una funzione cù una espressione cum'è u so corpu. Sì, cum'è mè, avete un gràficu di transizione di livellu bassu, u cumpunente di relooper vi aiuterà. In quantu capiscu, hè pussibule di utilizà un cuntrollu d'altu livellu di u flussu di esecutivu in un bloccu, finu à ch'ellu ùn va più di e fruntiere di u bloccu - vale à dì, hè pussibule di fà internu veloce / lento. ramificazione di u caminu in u codice di trasfurmazione di cache TLB integratu, ma micca per interferisce cù u flussu di cuntrollu "esternu". Quandu liberate un relooper, i so blocchi sò liberati, quandu liberate un modulu, l'espressioni, funzioni, ecc. arena.

In ogni casu, sè vo vulete interpretà u codice à a mosca senza creazione è eliminazione innecessaria di una istanza d'interprete, pò esse sensu di mette sta logica in un schedariu C++, è da quì gestisce direttamente l'API C++ intera di a biblioteca, sguassendu ready- fatti involucri.

Allora per generà u codice chì avete bisognu

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

... si aghju scurdatu di qualcosa, scusate, questu hè solu per rapprisintà a scala, è i dettagli sò in a documentazione.

È avà principia u crack-fex-pex, qualcosa cum'è questu:

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

Per cunnette in qualchì modu i mondi di QEMU è JS è à u stessu tempu accede à e funzioni compilate rapidamente, hè stata creata una matrice (una tabella di funzioni per impurtà in u lanciatore), è e funzioni generate sò stati posti quì. Per calculà rapidamente l'indici, l'indici di u bloccu di traduzzione di parola zero era inizialmente utilizatu cum'è questu, ma dopu l'indici calculatu cù sta formula hà cuminciatu à intruduce in u campu. struct TranslationBlock.

In modu, demo (attualmente cù una licenza oscura) funziona solu bè in Firefox. I sviluppatori di Chrome eranu in qualchì manera micca pronta à u fattu chì qualcunu vulia creà più di mille istanze di moduli WebAssembly, per quessa, anu semplicemente attribuitu un gigabyte di spaziu di indirizzu virtuale per ogni ...

Hè tuttu per avà. Forse ci sarà un altru articulu se qualcunu hè interessatu. Vale à dì, ci resta almenu solu fà u travagliu di i dispositi di bloccu. Puderia ancu sensu per fà a compilazione di moduli WebAssembly asincrona, cum'è abitudine in u mondu JS, postu chì ci hè ancu un interprete chì pò fà tuttu questu finu à chì u modulu nativu hè prontu.

Infine un enigma: avete cumpilatu un binariu nantu à una architettura di 32-bit, ma u codice, attraversu l'operazioni di memoria, scende da Binaryen, in qualchì locu nantu à a pila, o in un altru locu in u 2 GB superiore di u spaziu di indirizzu 32-bit. U prublema hè chì da u puntu di vista di Binaryen questu hè accede à un indirizzu risultatu troppu grande. Cumu ghjunghje in questu?

In modu di amministratore

Ùn aghju micca finitu di pruvà questu, ma u mo primu pensamentu era "E se aghju installatu Linux 32-bit?" Allora a parti suprana di u spaziu di indirizzu serà occupata da u kernel. A sola quistione hè quantu serà occupatu: 1 o 2 Gb.

In modu di programatore (opzione per i pratichi)

Lasciamu una bolla in cima di u spaziu di indirizzu. I stessu ùn capiscu perchè travaglia - quì digià ci deve esse una pila. Ma "semu praticanti: tuttu funziona per noi, ma nimu sà perchè..."

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

... hè vera chì ùn hè micca cumpatibile cù Valgrind, ma, per furtuna, Valgrind stessu spinge in modu assai efficace à tutti fora di quì :)

Forse qualcunu darà una spiegazione megliu di cumu funziona stu codice mio...

Source: www.habr.com

Add a comment