QEMU.js: sekarang serius dan dengan WASM

Sekali waktu, demi tawa, saya memutuskan untuk membuktikan reversibilitas proses dan belajar bagaimana menghasilkan JavaScript (atau lebih tepatnya, Asm.js) dari kode mesin. QEMU dipilih untuk percobaan, beberapa waktu kemudian sebuah artikel ditulis di Habr. Dalam komentar, saya disarankan untuk membuat kembali proyek di WebAssembly, dan saya sendiri tidak merasa ingin meninggalkan proyek yang hampir selesai sendiri ... Pekerjaan berjalan, tetapi sangat lambat, dan sekarang, dalam artikel itu, sebuah komentar muncul pada topik "Jadi bagaimana akhirnya?". Untuk jawaban terperinci saya, saya mendengar "Itu menarik artikel." Nah, jika menarik, akan ada artikel. Mungkin seseorang akan berguna. Dari sini, pembaca belajar beberapa fakta tentang perangkat yang menghasilkan kode QEMU, serta cara menulis kompiler Just-in-Time untuk aplikasi web.


Tugasnya


Karena saya sudah belajar cara "port" QEMU ke JavaScript, kali ini diputuskan untuk melakukannya dengan bijak dan tidak mengulangi kesalahan lama.


Kesalahan berapa kali: bercabang dari rilis titik


Kesalahan pertama saya adalah melakukan percabangan versi saya dari versi hulu 2.4.1. Lalu menurut saya ide yang bagus: jika rilis titik ada, maka mungkin lebih stabil daripada 2.4 sederhana, dan bahkan lebih dari master branch. Dan karena saya berencana untuk menambahkan bug yang cukup banyak, saya tidak membutuhkan orang asing sama sekali. Jadi itu mungkin terjadi. Tapi ini nasib buruk: QEMU tidak tinggal diam, dan pada titik tertentu mereka bahkan mengumumkan optimalisasi kode persen yang dihasilkan oleh 10. "Ya, sekarang aku kedinginan," pikirku dan putus . Di sini kita harus melakukan penyimpangan: karena sifat single-threaded dari QEMU.js dan fakta bahwa QEMU asli tidak menyiratkan tidak adanya multithreading (yaitu, sangat penting untuk dapat mengoperasikan beberapa jalur kode yang tidak terkait pada saat yang sama, dan bukan hanya "pasang semua kernel"), fungsi utama dari thread harus "berubah" untuk kemungkinan panggilan dari luar. Ini menciptakan beberapa masalah penggabungan alami. Namun, fakta bahwa beberapa perubahan dari cabang master , yang dengannya saya mencoba untuk menggabungkan kode saya, juga ceri dipilih dalam rilis titik (dan, karena itu, di cabang saya), juga, mungkin, tidak akan menambah kenyamanan.


Secara umum, saya memutuskan bahwa prototipe itu masuk akal membuang membongkar untuk bagian dan membangun versi baru dari awal berdasarkan sesuatu yang lebih segar dan sekarang dari master .


Kesalahan Nomor Dua: Metodologi TLP


Sebenarnya, ini bukan kesalahan, secara umum, ini hanya fitur membuat proyek dalam kondisi kesalahpahaman total tentang "ke mana dan bagaimana untuk pindah?", Dan secara umum "apakah kita akan sampai di sana?" Di bawah kondisi ini, pemrograman adalah pilihan yang dapat dibenarkan, tetapi, tentu saja, saya benar-benar tidak ingin mengulanginya secara tidak perlu. Kali ini saya ingin melakukannya dengan bijak: komit atom, perubahan kode yang disengaja (dan tidak “merangkai karakter acak bersama-sama sampai dikompilasi (dengan peringatan)”, seperti yang pernah dikatakan Linus Torvalds tentang seseorang, jika Anda percaya Wikitatnik), dll.


Kesalahan nomor tiga: tidak tahu harus naik ke air


Saya belum benar-benar menyingkirkan ini, tetapi sekarang saya memutuskan untuk tidak mengikuti jalan perlawanan paling sedikit, dan melakukannya "dengan cara dewasa", yaitu, menulis backend TCG saya dari awal sehingga saya tidak mengatakan nanti, "Ya, itu Tentu saja, pelan-pelan, tapi saya tidak bisa mengendalikan semuanya - TCI ditulis seperti itu ... ". Selain itu, awalnya ini sepertinya solusi yang jelas, karena saya menghasilkan kode biner . Seperti kata pepatah, "Saya mengumpulkan Ghent, tapi bukan yang itu": kodenya, tentu saja, biner, tetapi kontrol tidak dapat ditransfer begitu saja - itu perlu secara eksplisit didorong ke browser untuk kompilasi, menghasilkan objek tertentu dari dunia JS, yang masih perlu menyimpan di suatu tempat. Namun demikian, pada normal Untuk arsitektur RISC, seperti yang saya pahami, situasi tipikal adalah kebutuhan untuk secara otomatis membilas cache instruksi untuk kode yang dibuat ulang - jika ini bukan yang kita butuhkan, maka setidaknya itu sudah dekat. Selain itu, dari upaya terakhir saya, saya mengetahui bahwa kontrol tampaknya tidak ditransfer ke tengah-tengah blok terjemahan, oleh karena itu, kami tidak benar-benar memerlukan kode bytec ditafsirkan dari offset apa pun, dan kami hanya dapat menghasilkan dengan fungsi pada TB.


Datang dan menendang


Meskipun saya mulai menulis ulang kode pada bulan Juli, Pendel yang ajaib itu merayap tanpa disadari: biasanya surat-surat dari GitHub datang sebagai pemberitahuan tanggapan atas Masalah dan permintaan Tarik, dan kemudian, tiba-tiba , Binaryen sebagai backend qemu dalam konteks mengatakan , "Ini dia- dia melakukan sesuatu seperti itu, mungkin dia akan mengatakan sesuatu. " Itu tentang menggunakan perpustakaan Binaryen yang berhubungan dengan Emscripten untuk membuat JIT WASM. Yah, saya katakan bahwa Anda memiliki lisensi Apache 2.0 di sana, dan QEMU secara keseluruhan didistribusikan di bawah GPLv2, dan mereka tidak terlalu kompatibel. Tiba-tiba ternyata lisensi itu entah bagaimana bisa diperbaiki (saya tidak tahu: mungkin, berubah, mungkin melipatgandakan lisensi, mungkin sesuatu yang lain ...). Ini, tentu saja, membuat saya bahagia, karena saya sudah melihat format biner WebAssembly beberapa kali pada saat itu, dan entah bagaimana sedih dan tidak dapat dimengerti oleh saya. Ada perpustakaan di sini yang akan melahap blok dasar dengan grafik transisi, dan mengeluarkan bytecode, dan bahkan meluncurkannya di interpreter jika perlu.


Lalu ada juga surat di milis QEMU, tetapi ini lebih cenderung pada pertanyaan, "Siapa yang butuh itu?" Dan itu, tiba-tiba , diperlukan. Minimal, Anda dapat mengikis bersama kasus penggunaan tersebut jika berfungsi lebih atau kurang cerdas:


  • meluncurkan pengajaran apa pun tanpa instalasi sama sekali
  • virtualisasi di iOS, di mana menurut rumor satu-satunya aplikasi yang memiliki hak untuk membuat kode on the fly adalah mesin JS (apakah itu benar?)
  • demonstrasi mini-OS - disk tunggal, bawaan, semua jenis firmware, dll ...

Fitur runtime browser


Seperti yang saya katakan, QEMU terkait dengan multithreading, tetapi tidak di browser. Ya, itu seperti, tidak ... Awalnya tidak ada sama sekali, kemudian WebWorkers muncul - seperti yang saya pahami, ini multithreading berdasarkan pesan yang lewat tanpa variabel variabel yang saling terkait. Secara alami, ini menciptakan masalah signifikan ketika porting kode yang ada berdasarkan pada model memori bersama. Kemudian, di bawah tekanan dari publik, itu diimplementasikan dengan nama SharedArrayBuffers . Mereka secara bertahap memperkenalkannya, merayakan peluncurannya di browser yang berbeda, kemudian merayakan tahun baru, dan kemudian Meltdown ... Setelah itu mereka sampai pada kesimpulan bahwa pengukuran waktu yang kasar, tidak kasar, tetapi dengan bantuan memori bersama dan aliran yang menambah penghitung, itu masih cukup akurat . Jadi mereka mematikan multithreading dengan memori bersama. Tampaknya mereka kemudian menyalakannya kembali, tetapi, setelah menjadi jelas dari percobaan pertama, ada kehidupan tanpa itu, dan jika demikian, kami akan mencoba melakukannya tanpa mengandalkan multithreading.


Fitur kedua adalah ketidakmungkinan manipulasi tingkat rendah dengan tumpukan: Anda tidak bisa hanya mengambil, menyimpan konteks saat ini dan beralih ke yang baru dengan tumpukan baru. Tumpukan panggilan dikelola oleh mesin virtual JS. Tampaknya, apa masalahnya, karena kami masih memutuskan untuk mengelola aliran yang sebelumnya sepenuhnya secara manual? Faktanya adalah bahwa blok input-output dalam QEMU diimplementasikan melalui coroutine, dan di sini manipulasi tumpukan tingkat rendah akan berguna bagi kita. Untungnya, Emscipten sudah mengandung mekanisme untuk operasi asinkron, bahkan dua: Asyncify dan Emterpreter . Yang pertama bekerja melalui kembung signifikan dari kode JavaScript yang dihasilkan dan tidak lagi didukung. Yang kedua adalah "cara yang benar" saat ini dan bekerja melalui generasi bytecode untuk penerjemahnya sendiri. Ini berfungsi, tentu saja, secara perlahan, tetapi itu tidak mengembang kode. Benar, dukungan coroutine untuk mekanisme ini harus dikaitkan sendiri (sudah ada coroutine yang ditulis di bawah Asyncify dan ada implementasi API yang kira-kira sama untuk Emterpreter, Anda hanya perlu menghubungkannya).


Saat ini, saya belum berhasil membagi kode menjadi dikompilasi dalam WASM dan ditafsirkan menggunakan Emterpreter, sehingga perangkat blok belum berfungsi (lihat seri berikutnya, seperti yang mereka katakan ...). Artinya, pada akhirnya, Anda harus mendapatkan sesuatu yang lucu seperti lapisan:


  • blok I / O yang diartikan Nah, apa, apakah Anda benar-benar mengharapkan NVMe yang ditiru dengan kinerja asli? :)
  • kode QEMU utama yang dikompilasi secara statis (penerjemah, perangkat lain yang diemulasikan, dll.)
  • WASM mengkompilasi kode tamu secara dinamis

Fitur sumber QEMU


Seperti yang mungkin sudah Anda tebak, kode emulasi untuk arsitektur tamu dan kode untuk menghasilkan instruksi mesin host dari QEMU terpisah. Bahkan, ada yang sedikit lebih rumit:


  • ada arsitektur tamu
  • ada akselerator , yaitu, KVM untuk virtualisasi perangkat keras di Linux (untuk sistem host dan host yang kompatibel), TCG untuk pembuatan kode JIT di mana saja. Dimulai dengan QEMU 2.9, dukungan untuk standar virtualisasi perangkat keras HAXM pada Windows muncul ( detail )
  • jika TCG digunakan, dan bukan virtualisasi perangkat keras, maka TCG memiliki dukungan terpisah untuk pembuatan kode untuk setiap arsitektur host, serta untuk interpreter universal
  • ... dan sekitarnya - periferal yang ditiru, antarmuka pengguna, migrasi, rekam ulangan, dll.

Omong-omong, tahukah Anda: QEMU tidak hanya dapat mengemulasi seluruh komputer, tetapi juga prosesor untuk proses pengguna terpisah di kernel host, yang digunakan, misalnya, oleh fuzzer AFL untuk instrumentasi binari. Mungkin seseorang ingin mem-porting mode operasi QEMU ini ke JS? ;)


Seperti kebanyakan program gratis yang sudah lama ada, QEMU dibangun melalui panggilan untuk configure dan make . Misalkan Anda memutuskan untuk menambahkan sesuatu: backend TCG, implementasi utas, sesuatu yang lain. Jangan terburu-buru untuk bersukacita / ngeri (garis bawahi seperlunya) prospek berkomunikasi dengan Autoconf - pada kenyataannya, configure di QEMU tampaknya ditulis sendiri dan tidak ada yang menghasilkan dari.


Perakitan web


Jadi apa benda ini - WebAssembly (alias WASM)? Ini adalah pengganti Asm.js, sekarang tidak lagi berpura-pura menjadi kode JavaScript yang valid. Sebaliknya, itu murni biner dan dioptimalkan, dan bahkan hanya menulis bilangan bulat ke dalamnya tidak terlalu sederhana: disimpan dalam format LEB128 untuk kekompakan .


Anda mungkin pernah mendengar tentang algoritma reload untuk Asm.js - memulihkan instruksi kontrol aliran eksekusi "tingkat tinggi" (yaitu, jika-maka-yang lain, loop, dll.) Di mana mesin JS disetel dari LLVM IR tingkat rendah, lebih dekat ke kode mesin yang dijalankan oleh prosesor. Secara alami, representasi perantara QEMU lebih dekat ke yang kedua. Tampaknya di sinilah, bytecode, akhir dari siksaan ... Dan kemudian blok, jika-maka-lain dan loop! ..


Dan ini adalah alasan lain mengapa Binaryen berguna: ia tentu saja dapat menerima blok tingkat tinggi dekat dengan apa yang akan disimpan dalam WASM. Tetapi juga dapat menghasilkan kode dari grafik blok dasar dan transisi di antara mereka. Yah, saya sudah mengatakan bahwa menyembunyikan format penyimpanan WebAssembly di belakang API C / C ++ yang nyaman.


TCG (Tiny Code Generator)


TCG awalnya merupakan backend untuk kompiler C. Kemudian, tampaknya, ia tidak tahan persaingan dengan GCC, tetapi pada akhirnya ia menemukan tempatnya di QEMU sebagai mekanisme pembuatan kode untuk platform host. Ada juga backend TCG yang menghasilkan beberapa bytecode abstrak, yang segera dieksekusi oleh penerjemah, tetapi saya memutuskan untuk pergi kali ini. Namun, fakta bahwa QEMU sudah memiliki kemampuan untuk mengaktifkan transisi ke TB yang dihasilkan melalui fungsi tcg_qemu_tb_exec sangat membantu saya.


Untuk menambahkan backend TCG baru ke QEMU, Anda perlu membuat tcg/< > (dalam hal ini, tcg/binaryen ), dan ada dua file di dalamnya: tcg-target.h dan tcg-target.inc.c dan mendaftarkan semuanya ini configure . Anda dapat meletakkan file-file lain di sana, tetapi, seperti yang dapat Anda tebak dari nama-nama keduanya, keduanya akan dimasukkan di suatu tempat: satu sebagai file header biasa (itu akan dimasukkan dalam tcg/tcg.h , dan yang sudah ada dalam file lain di direktori tcg , accel dan bukan hanya), yang lain hanya sebagai potongan kode di tcg/tcg.c , tetapi memiliki akses ke fungsi statisnya.


Setelah memutuskan bahwa saya akan menghabiskan terlalu banyak waktu pada proses terperinci, cara kerjanya, saya cukup menyalin "kerangka" dari dua file ini dari implementasi backend lain, dengan jujur ​​menunjukkan ini di header lisensi.


File tcg-target.h terutama berisi pengaturan dalam bentuk #define s:


  • berapa banyak register dan seberapa luas pada arsitektur target (kami memiliki - sebanyak yang kami inginkan, ada begitu banyak - pertanyaannya adalah lebih dari apa yang akan dihasilkan oleh browser dalam kode yang lebih efisien pada arsitektur "sepenuhnya target" ...)
  • penyelarasan instruksi host: pada x86, dan di TCI, instruksinya tidak sejajar sama sekali, tapi saya akan memasukkan buffer kode bukan instruksi sama sekali, tetapi menunjuk ke struktur perpustakaan Binaryen, jadi saya akan mengatakan: 4 byte
  • instruksi opsional apa yang dapat dihasilkan backend - nyalakan semua yang kami temukan di Binaryen, biarkan akselerator memecah sisanya menjadi yang lebih sederhana
  • perkiraan ukuran cache TLB yang diminta oleh backend. Faktanya adalah bahwa dalam QEMU semuanya serius: meskipun ada fungsi pembantu yang memuat / menyimpan dengan mempertimbangkan MMU tamu (dan di mana sekarang tanpa itu?), Mereka menyimpan cache terjemahan mereka dalam bentuk struktur, proses yang nyaman untuk ditanamkan langsung ke blok terjemahan. Pertanyaannya adalah, offset apa dalam struktur ini yang paling efisien ditangani oleh urutan perintah yang kecil dan cepat
  • di sini Anda dapat memutar tujuan satu atau dua register yang dipesan, mengaktifkan panggilan TB melalui suatu fungsi dan secara opsional menggambarkan beberapa fungsi inline kecil seperti flush_icache_range (tetapi ini bukan kasus kami)

File tcg-target.inc.c , tentu saja, biasanya jauh lebih besar dan berisi beberapa fungsi yang diperlukan:


  • inisialisasi, menunjukkan, antara lain, pembatasan instruksi mana yang dapat digunakan operan. Tidak sengaja saya salin dari backend lain
  • fungsi menerima satu instruksi bytecode internal
  • di sini Anda dapat meletakkan fungsi bantu, dan juga di sini Anda dapat menggunakan fungsi statis dari tcg/tcg.c

Bagi saya sendiri, saya memilih strategi berikut: dalam kata-kata pertama dari blok terjemahan berikutnya, saya menuliskan empat petunjuk: tanda awal (nilai tertentu di sekitar 0xFFFFFFFF , yang menentukan status TB saat ini), konteks, modul yang dihasilkan, dan angka ajaib untuk debugging. Pertama, label ditetapkan ke 0xFFFFFFFF - n , di mana n adalah angka positif yang kecil, dan setiap kali melalui penerjemah, angka tersebut bertambah 1. Ketika mencapai 0xFFFFFFFE , kompilasi terjadi, modul disimpan di tabel fungsi, diimpor ke “peluncur” kecil, ke mana eksekusi meninggalkan tcg_qemu_tb_exec , dan modul dihapus dari memori QEMU.


Mengutip klasik, "Crutch, berapa banyak proger telah terjalin dalam suara ini untuk jantung ...". Namun, memori itu bocor entah ke mana. Dan itu adalah memori yang dikelola oleh QEMU! Saya punya kode yang, ketika menuliskan instruksi selanjutnya (yah, itu adalah sebuah pointer), menghapus satu tautan yang ada di tempat ini sebelumnya, tetapi itu tidak membantu. Sebenarnya, dalam kasus paling sederhana, QEMU mengalokasikan memori saat startup dan menulis kode yang dihasilkan di sana. Ketika buffer berakhir, kode tersebut dibuang, dan yang berikutnya mulai ditulis di tempatnya.


Setelah mempelajari kodenya, saya menyadari bahwa tongkat penyangga dengan angka ajaib memungkinkan kami untuk tidak jatuh pada kehancuran tumpukan, membebaskan sesuatu yang salah pada buffer yang tidak diinisialisasi pada lintasan pertama. Tetapi siapa yang akan menimpa penyangga melewati fungsi saya nanti? Seperti yang disarankan pengembang Emscripten, setelah mengalami masalah, saya mem-porting kode yang dihasilkan kembali ke aplikasi asli, mengatur Mozilla Record-Replay di atasnya ... Secara umum, sebagai hasilnya, saya menyadari hal sederhana: struct TranslationBlock dengan deskripsi dialokasikan untuk setiap blok. Tebak di mana ... Itu benar, tepat di depan blok tepat di buffer. Setelah menyadari ini, saya memutuskan untuk mengikatnya dengan kruk (setidaknya beberapa), dan cukup membuang angka ajaib, dan mentransfer kata-kata yang tersisa ke struct TranslationBlock , membuat daftar tautan tunggal yang dapat Anda lalui dengan cepat saat mengatur ulang cache terjemahan dan membebaskan memori.


Beberapa kruk tetap: misalnya, penunjuk yang ditandai di buffer kode - beberapa di antaranya hanya BinaryenExpressionRef , yaitu, mereka melihat ekspresi yang perlu dimasukkan secara linear ke dalam unit dasar yang dihasilkan, sebagian - kondisi transisi antara WB, bagian - ke mana harus pergi. Nah, sudah ada blok yang disiapkan untuk Relooper, yang harus terhubung sesuai dengan kondisi. Untuk membedakannya, asumsi digunakan bahwa mereka semua disejajarkan setidaknya empat byte, sehingga Anda dapat menggunakan dua bit label dengan aman, Anda hanya perlu mengingat untuk menghapusnya jika perlu. Omong-omong, label semacam itu sudah digunakan di QEMU untuk menunjukkan alasan untuk keluar dari siklus TCG.


Menggunakan Binaryen


Modul di WebAssembly berisi fungsi, yang masing-masing berisi tubuh yang mewakili ekspresi. Ekspresi adalah operasi unary dan biner, blok yang terdiri dari daftar ekspresi lain, aliran kontrol, dll. Seperti yang sudah saya katakan, aliran kendali di sini diatur persis seperti cabang tingkat tinggi, loop, pemanggilan fungsi, dll. Argumen untuk fungsi dilewatkan bukan pada stack, tetapi secara eksplisit, seperti pada JS. Ada variabel global, tetapi saya tidak menggunakannya, jadi saya tidak akan membicarakannya.


Fungsinya juga memiliki variabel lokal, dinomori dari awal, dengan tipe: int32 / int64 / float / double. Variabel lokal n pertama adalah argumen yang diteruskan ke fungsi. Harap dicatat bahwa meskipun semuanya di sini tidak sepenuhnya level rendah dalam hal aliran kontrol, bilangan bulat masih tidak membawa tanda / tanda tanpa tanda: bagaimana nomor akan berperilaku tergantung pada kode operasi.


Secara umum, Binaryen menyediakan C-API sederhana : Anda membuat modul, di dalamnya Anda membuat ekspresi - unary, binary, blok dari ekspresi lain, aliran kontrol, dll. Kemudian Anda membuat fungsi, yang tubuhnya Anda perlu menentukan ekspresi. Jika Anda, seperti saya, memiliki grafik transisi level rendah, komponen relooper akan membantu Anda. Sejauh yang saya mengerti, adalah mungkin untuk menggunakan kontrol tingkat tinggi dari aliran eksekusi di blok, selama itu tidak melampaui batas blok - yaitu, adalah mungkin untuk membuat jalur cepat internal / cabang jalur lambat di dalam kode pemrosesan cache TLB built-in, tetapi tidak ada gangguan dengan aliran kontrol "eksternal" . Ketika Anda melepaskan relooper, bloknya dilepaskan, ketika Anda melepaskan modul, ekspresi, fungsi, dll., Yang dialokasikan di arena menghilang.


Namun, jika Anda ingin menafsirkan kode saat bepergian tanpa membuat dan menghapus instance interpreter yang tidak perlu, mungkin masuk akal untuk mentransfer logika ini ke file C ++, dan dari sana langsung mengontrol seluruh perpustakaan C ++ API, mem-bypass pembungkus yang sudah jadi.


Jadi, untuk menghasilkan kode, Anda perlu


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

… — , , — .


--, :


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

- QEMU JS , ( ), . , translation block, , struct TranslationBlock .


, ( ) Firefox. Chrome - , - WebAssembly, ...


. , , - . , . , WebAssembly , JS, , , .


: 32- , Binaryen, - - 2 32- . , Binaryen . ?


-

Saya tidak menguji ini pada akhirnya, tetapi pikiran pertama saya adalah "Bagaimana jika saya meletakkan Linux 32-bit?" Kemudian bagian atas dari ruang alamat akan ditempati oleh kernel. Satu-satunya pertanyaan adalah berapa banyak yang akan ditempati: 1 atau 2 Gb.


- ( )

Kami mengembang gelembung di bagian atas ruang alamat. Saya sendiri tidak mengerti mengapa ini bekerja - seharusnya sudah ada tumpukan di tempat yang sama . Tapi "kami adalah praktisi: semuanya bekerja untuk kami, tetapi tidak ada yang tahu mengapa ...".


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

... itu tidak kompatibel dengan Valgrind, tetapi, untungnya, Valgrind sangat efektif dalam memadatkan semua orang dari sana :)


Mungkin seseorang akan memberikan penjelasan yang lebih baik tentang bagaimana kode saya berfungsi ...

Source: https://habr.com/ru/post/id451306/


All Articles