blink - 最も小さなx86-64-Linuxエミュレータ

(tiniest x86-64-linux emulator)

Created at: 2022-11-03 14:07:09
Language: C
License: ISC

GCC 9.4.0を実行しているBlinkのスクリーンショット

ブリンケンライト

このプロジェクトには、次の 2 つのプログラムが含まれています。

blink
は、静的にコンパイルされた x86-64-Linux を実行する仮想マシンです。 さまざまなオペレーティングシステムとハードウェアアーキテクチャ上のプログラム。それは (a)を除いて、コマンドと同じことを行うように設計されています Blinkは4MBのバイナリではなく、~160kbのフットプリントしかありません。そして (b)Blinkは、エミュレートなどの一部のベンチマークでQemuよりも2倍速くなります GCC.トレードオフは、BlinkにはQemuほど多くの機能がないことです。瞬く 非常に小さい仮想マシンが必要な場合に最適です 一時的なプログラムをはるかに高速に実行します。詳細については、 このツールの動機は、https://justine.lol/ape.html お読みください。
qemu-x86_64

ブリンケンライトはTUIインターフェイスです これは、プラットフォーム間でx86_64-Linuxプログラムをデバッグするために使用できます。 GDBとは異なり、ブリンケンライトはプログラムの実行を視覚化することに重点を置いています。それ UNICODE IBM コード・ページ 437 文字を使用してバイナリー・メモリーを表示します。 パネルは、プログラムのアセンブリ コードをステップ実行すると変化します。 これらのメモリパネルは、マウスホイールを使用してスクロールおよびズームできます。 Blinkenlightsは、スクロールホイールオーバーするリバースデバッグも可能にします アセンブリ表示では、実行履歴を巻き戻すことができます。

はじめ

Blinkがx86-64-linuxバイナリを実行できることを定期的にテストしています。 次のプラットフォーム:

  • Linux (x86, ARM, RISC-V, MIPS, POWERPC, S390x)
  • MacOS (x86, ARM)
  • フリーBSD
  • OpenBSD
  • ネットBSD

Blink は、次のライブラリによって異なります。

  • libc (POSIX.1-2017)

Blink は、次のような UNIX システムでコンパイルできます。

  • C11コンパイラ(GCC 4.9.4+など)
  • 現代のGNU Make(つまり、XCodeに付属しているものではありません)

Blink をコンパイルする手順は次のとおりです。

$ make -j4
$ o//blink/blink -h
Usage: o//blink/blink [-hjms] PROG [ARGS...]
  -h        help
  -j        disable jit
  -m        enable memory safety
  -s        print statistics on exit

Blinkで簡単なhello worldプログラムを実行する方法は次のとおりです。

o//blink/blink third_party/cosmo/tinyhello.elf

Blink には、UTF-8 ANSI 端末で動作するデバッガー TUI があります。ザ このインターフェイスの最も重要なキーストロークは、ヘルプ用です。 ステップ、続行、および逆デバッグのためのスクロールホイール。

?
s
c

o//blink/blinkenlights third_party/cosmo/tinyhello.elf

テスティング

Blinkは、主にプリコンパイルされたx86バイナリを使用してテストされます。 自動的にダウンロードされます。あなたはBlinkがあなたの上でどれだけうまく機能するかをチェックすることができます 以下を実行してローカルプラットフォーム:

make check

Blinkが11の異なるハードウェアで動作することを確認するには(Makefileを参照)、次のコマンドを実行します。 GCCとQemuの静的にコンパイルされたビルドをダウンロードします。私たちの ツールチェーンバイナリはx86-64 Linuxを対象としており、Blinkはブートストラップします 最初にローカルでこれらのテストを実行できるようにする オペレーティング システムとアーキテクチャ。

$(ARCHITECTURES)

make check2
make emulates

代替ビルド

パフォーマンスを最大にするには、または を使用します。ご注意ください リリース モードのビルドでは、すべてのログ記録ステートメントとアサーション ステートメントが削除されます そして、Blinkはまだそのために十分に成熟していません。したがって、特別な注意が必要です。

MODE=rel
MODE=opt

make MODE=rel
o/rel/blink/blink -h

最大にするには、.このビルドモードは、 ロギングステートメントとアサーションステートメントを削除しますが、パフォーマンスも低下させます 可能な限り、より小さなバイナリサイズを優先します。

MODE=tiny

make MODE=tiny
strip o/tiny/blink/blink
ls -hal o/tiny/blink/blink

Blink のバグは、次のビルドモードを使用して追跡できます。

  • MODE=asan
    メモリの安全性のバグを見つけるのに役立ちます
  • MODE=tsan
    スレッド関連のバグを見つけるのに役立ちます
  • MODE=ubsan
    C標準の違反を見つけるには
  • MODE=msan
    初期化されていないメモリエラーを見つけるのに役立ちます

Blinkでのプログラムのコンパイルと実行

Blinkは、特にどのLinux実行可能ファイルをエミュレートするかについてうるさいです Apple M1のようなプラットフォームで実行している間。たとえば、アップルM1には システムページサイズは16kbで、WASMのページサイズは64kbです。それは作る Linuxのため、すべてのLinuxプログラムの完全なエミュレーションは不可能です プログラムは通常、4096バイトのページのようなものを想定しています。最も明白な これが引き起こす障害は、ELFの読み込みプロセスで発生します。 VADRオフセットスキューについて文句を言うかもしれません:

I2023-01-06T18:12:51.007788:blink/loader.c:91:47550 p_vaddr p_offset skew unequal w.r.t. host page size

解決策は、GCCフラグを使用してプログラムを再コンパイルすることです。

gcc -static -Wl,-z,common-page-size=65536,-z,max-page-size=65536 ...

しかし、それはほんの一歩です。プログラムはまた、4096を想定するのではなく、次のようなAPIを使用する必要があります。 ページサイズを取得するための標準API。Cライブラリはこれを取得します を介してCライブラリにそれを提供するBlinkからの情報。

sysconf(_SC_PAGESIZE)
getauxval(AT_PAGESZ)

ブリンケンライトデバッガTUIを使用している場合は、別の重要な 使用するフラグのセットは次のとおりです。

  • -fno-omit-frame-pointer
  • -mno-omit-leaf-frame-pointer

デフォルトでは、GCCとClangはバックトレースポインタを一般的なものとして使用します 目的登録のため、ブリンケンライトは表示できません 呼び出し履歴を視覚化するフレームパネル。これらのフラグを使用すると、それが解決されます。 しかし、複合体でそれらを正しく指定するのは難しい場合があります 他の最適化フラグが後で発生する可能性のあるビルド環境 それらを再びオフにします。

%rbp

プログラムのコンパイルに使用することをお勧めするトリックは、 コンパイラー・コマンドをラップし、スクリプトを使用するシェル・スクリプト 環境変数で。スクリプトは次のようになります 次のように:

$CC

#!/bin/sh
exec cc \
  -g \
  -Os \
  -no-pie \
  -fno-pie \
  -static \
  "$@" \
  -U_FORTIFY_SOURCE \
  -fno-stack-protector \
  -fno-omit-frame-pointer \
  -mno-omit-leaf-frame-pointer \
  -Wl,-z,common-page-size=65536 \
  -Wl,-z,max-page-size=65536

これらのフラグは、Linuxバイナリを支援するのに大いに役立ちます (1)サポートされているすべての動作でBlinkで実行できる システムとマイクロプロセッサアーキテクチャ、および(2)の一部を取引する アセンブリを作るために現代のセキュリティブランケット パネルはより読みやすく、メモリについてうるさい可能性は低くなります。

コスモポリタンLibcユーザーの場合、コスモポリタンはすでに提供しています そのようなスクリプト、それはとツールチェーンです。お願いします コスモポリタンLibcは64kbのページサイズを使用しているため、影響を受けないことに注意してください GlibcとMuslのユーザーが経験する可能性のあるこれらの問題の多くによって。

cosmocc
cosmoc++

C / C ++開発者ではなく、高レベルを使用したい場合 言語 代わりに、エミュレートすることを検討するプログラムの1つは 実際にはポータブルPython、これはCPython v3.6のAPEビルドです 通訳。ソースからビルドし、次のように使用できます。

git clone https://github.com/jart/cosmopolitan/
cd cosmopolitan
make -j8 o//third_party/python/python.com
blinkenlights -jm o//third_party/python/python.com

フラグはブリンケンライトTUIに尋ねるので、ここで役立ちます をクリックして、JIT と線形メモリ最適化を有効にします。持っていると便利です Pythonは非常に複雑で計算集約的であるため、これらのフラグ プログラム、そうでなければブリンケンライトの下で動きが遅すぎるでしょう ビジュアライゼーション。(TURBO)キーを数回押すこともできます PythonをTUIでさらに高速にエミュレートできるようにするためです。

-jm
CTRL-T

あなたが試すことができる他のいくつかのプログラムは、SQLiteとAntirezのキロエディタです。

git clone https://github.com/jart/cosmopolitan/
cd cosmopolitan
make -j8 o//third_party/sqlite3/sqlite3.com
blinkenlights -jm o//third_party/sqlite3/sqlite3.com
make -j8 o//examples/kilo.com
blinkenlights -jm o//examples/kilo.com

詳細については、コスモポリタン入門をお読みください。 Libcは、独自のプログラムを作成する方法を説明するブログ投稿です。 当然のことながら機能が保証されるコスモポリタンモノレポ ブリンクとブリンケンライトの下で本当にうまくいきます。

技術的な詳細

blinkは、POSIXプラットフォーム用のx86-64インタプリタです。 C++ コンパイラと互換性のある ANSI C11。命令デコードは Intelの逆アセンブラXedのトリミングされたバージョンを使用して行われます。

このプロジェクトの主な指示は、仮想マシンとして機能することです。 コスモポリタンLibcによってコンパイルされたユーザースペースバイナリ。しかし、私たちも持っていました GlibcとMusl Libcでコンパイルされたプログラムの仮想化に成功しました。 GCCとケム。Blink は 130+ Linux システムコール ABI をサポートしています。 fork() と clone() を使用します。Linuxシステムコールはロングモードでのみ使用できます システムVに書かれている命令を介したプログラム アビ。

SYSCALL

命令セット

Blink では、次のハードウェア ISA がサポートされています。

  • i8086
  • i386
  • X87
  • SSE2
  • x86_64
  • SSE3
  • SSSE3
  • ティッカー
  • ポプトン・ポプトン
  • ティッカー
  • BMI2
  • ティッカー
  • ティッカー
  • ティッカー

プログラムは、オプションの有無を確認するために使用できます 命令セット。Blinkは同じものに従わないことに注意してください インテルのハードウェアとしての単調な進歩。たとえば、BMI2 がサポートされています。 これはAVX2でエンコードされた(VEX)命令セットであり、Blinkはこれが可能です AVX2 ISA がサポートされていない場合でも、デコードします。したがって、それは ISAを「レベル」にグロブしないことが重要です(Windowsソフトウェアは do)BMI2のサポートがAVX2のサポートを意味すると想定されている場合。というのは 現在そうではないBlinkで。

CPUID

一方、BlinkはWindowsのx87の動作を共有しています。 (長い倍精度ではなく)精度。80ビットは使用できません Blinkは単に通過するため、Blinkによる浮動小数点精度 ホストアーキテクチャへの浮動小数点演算、およびごくわずか アーキテクチャは精度をサポートします。あなたはまだx87を使うことができます 80ビットワードで。点滅は64ビット浮動小数点値を格納するだけです それらの内部にあり、それはx87 FPUによる合法的な構成です コントロールワード。可能であれば、単に 回避。64ビット浮動小数点がロケットに十分である場合 の科学者 NASAなら、それは誰にとっても十分なはずです。いくつかの独特のものがあります アーキテクチャ間での動作の違い(点滅 現在、対処することは何もしていません)が、比較的 マイナー、たとえば、の代わりにOPが返される。

long double
long double
double
NAN
-NAN

Blinkは、ベースラインISAを合理的に包括的にカバーしています。 BCD操作のサポートも含まれます(ロングモードでも!しかし、そこに は、Blinkが実装していない、 や など、本当にフリンジ命令です。サポートされていない手順のほとんどは、通常、 リング 0 システム命令 (Blink は主にユーザー モード VM であるため) そのため、ベア メタル オペレーティング システムのサポートは限られています ソフトウェア(これについては、後のセクションで詳しく説明します)。

BOUND
ENTER

まばたき自体は、ベンダーを確認することで検出できます 文字列(通常報告される/ブランドではなく)。古いバージョンの ブリンケンライトはブランドを使用しています。まばたきも自分自身を識別します システムコール経由として。虚偽のバージョンを報告します そうしないと、Glibcをリンクするすべてのプログラムが実行を拒否するためです。

CPUID
GenuineBlink
GenuineIntel
AuthenticAMD
GenuineCosmo
blink 4.0
uname()

ジット

Blinkは、x86_64でサポートされているジャストインタイムコンパイルを使用します。 アーチ64。Blink は、制限を回避するために適切な手順を実行します。 アップルやOpenBSDのようなプラットフォームでのJITに関連しています。JITを生成します printf スタイルのドメイン固有言語を使用したコード。JITは以下によって機能します マイクロOP関数を呼び出す実行時に関数を生成する コンパイラが作成されました。マイクロオペレーションを高速化するために、Blinkは 実行時にコンパイルされた関数のバイト長は、 RET命令。Blinkはコンパイルされた関数をにコピーします JIT が生成している関数。ただし、これはほとんどの場合に機能します 一部のツールは問題を引き起こす可能性があります。たとえば、OpenBSD RetGuard は 静的メモリは、コンパイルされたすべての関数に再配置されます。 JITは現在理解していません。したがって、コンパイラフラグを使用して そのタイプの魔法を無効にします。他のそのような魔法がすり抜けた場合、 Blinkには、明らかな問題をキャッチするランタイムチェックがあり、 CALL 命令の使用に正常にフォールバックします。JITはできないので すべてのプラットフォームで完全に完璧で、フラグは Blink の JIT を無効にするために渡されました。JITを無効にすると、 点滅は10倍遅くなります。このコマンドを使用すると、フラグは反対の意味になり、代わりに JIT が有効になります。これ TUI ディスプレイには JITパス形成を可視化できる機能。現在点滅のみ ロングモード(64ビット)で実行されているプログラムのJITを有効にしますが、 将来的には 16 ビット プログラムの JIT 処理をサポートします。

o//blink/blink -j
o//blink/blinkenlights
-j

Virtualization

Blinkは、ハードウェアと同じPML4Tアプローチを使用してメモリを仮想化します それ自体、メモリルックアップは4レベルの基数を介してリダイレクトされます 木。すべてのメモリで4つの別々のページテーブルルックアップを実行して以来 アクセスが遅くなる可能性があります、Blinkは変換ルックアサイドバッファをチェックします。 最近使用した 16 個のページ テーブル エントリが含まれます。The PML4T Blinkのすべてのメモリルックアップを「安全」にすることができますが、それでもそうではありません 可能な限り最高のパフォーマンスを提供します。したがって、巨大なシステムでは アドレス空間(つまり、ペタバイトの仮想メモリ)点滅はそれ自体に依存しています ランダムな場所にロードされ、ID がゲスト メモリをマップする 単純な線形変換を使用します。たとえば、ゲスト仮想 アドレスは、ホストアドレスは.これは、メモリ操作のたびに が実行されると、単純な追加のみを実行する必要があります。これは行きます 非常に高速ですが、を使用するプログラムでは問題が発生する可能性があります。最新のラズベリーパイなどの一部のシステムには、実際には x86-64よりも大きなアドレス空間により、Blinkはゲストに 完全なアドレス空間。ただし、32ビットプラットフォームなどの一部のプラットフォームでは、 使用できる ID マッピングの数は限られています。もあります 固定アドレスの多くを主張するTSANのようなコンパイラツール 間。Blinkのソリューションは、コスモポリタンのニーズを満たすように設計されています Libc、32ビットアドレスに対するAppleの制限を回避している間、および ASANの制限との完全な互換性を維持します。イベントで この変換スキームがシステムで機能しないこと、線形変換の最適化を無効にするフラグが渡される可能性があります。 代わりに、メモリ セーフな完全仮想化アプローチのみを使用します。 PML4TおよびTLB。

0x400000
0x400000+0x088800000000
MAP_FIXED
blink -m

Blink には xterm 互換の ANSI テレタイプライターディスプレイ実装があります これにより、BlinkのTUIインターフェイスは、 組み込み端末ディスプレイ。たとえば、アンティレスの BlinkのTUI内のキロテキストエディタ。

Blink は SectorLISP などの 16 ビット BIOS プログラムをサポートしています。リアルに起動するには Blinkのモードプログラムの場合、フラグは次のようになります。 passed は、仮想マシンを i8086 モードにします。現在、 使用できる BIOS API のセットは限られています。たとえば、Blink は IBM をサポートしています。 レンダリングされる PC シリアル UART、CGA ディスプレイ、および MDA ディスプレイ API TUI インターフェイスでブロック文字を使用する。私たちは私たちの本当の拡大を望んでいます 近い将来、次のようなオペレーティングシステムを実行するためのモードサポート エルクス。

o//blink/blinkenlights -r

Blinkは、オペレーティングシステムのブートローダーのトラブルシューティングをサポートしています。まばたきは それぞれにオペレーティングシステムを埋め込むコスモポリタンLibc用に設計されています バイナリはコンパイルします。Blinkは、ベアメタルサポートのデバッグに役立ちました。 Blinkは16ビット、32ビット、および64ビットで実行できるため ブートローダーがさまざまな段階で必要とするモード。そのために、 いくつかのring0ハードウェア命令を実装する必要がありました。まばたきで十分です コスモポリタンをサポートしますが、Blinkを Windowsのようなものを起動できる場所を指します。

Blinkはいくつかの異なる実行可能フォーマットをサポートしています。 静的。以下を実行できます。

  • 実際にはポータブル実行可能ファイルで、または魔法のいずれかを持っています。

    MZqFpD
    jartsr

  • 静的にコンパイルされた x86-64-linux ELF 実行可能ファイル (ただし、 PIC / PIEを使用したり、通訳を要求したりしないでください。

  • フラットな実行可能ファイル。ファイル拡張子で終わる必要があります。で この場合、実行可能ファイルのサイズを10バイトまで小さくすることができます。 それらは生のx86-64コードとして扱われるためです。点滅は常にフラットにロードされます 実行可能ファイルをアドレスに追加し、自動的に16MBを追加します BSSメモリの。

    .bin
    0x400000

  • アドレスにロードされるリアルモードの実行可能ファイル .これら プログラムは、フラグを指定したコマンドを使用して実行する必要があります。

    0x7c00
    blinkenlights
    -r