公開: 2026年1月30日 最終更新: 2026年1月30日 永続リンク
自分がやりたかったことの一つに、アセンブリ言語でまとまった量のプログラムを書くというものがありました。いや別に誰も止めないし書けばいいじゃんというのはそうなのですが、うっかりそこそこ大きなプログラムになってしまったときに、他のプラットフォームへの移植が難しくなってしまうことを懸念して二の足を踏んでいました。しかし最近、昨今の発達した仮想化技術を用いれば移植可能になるのでは?と思い、思い立って移植可能なアセンブリプログラムの開発を試みることにしました。前提として、本記事で書くアセンブリプログラムの直接の対象プラットフォームはx86_64 Linuxのユーザー空間です。何故なら生活環境。組み込み開発やレトロコンピューティング、システムプログラミングをするわけではありません。いちUnixアプリケーションをアセンブリ言語で書いてみたいということです。
.intel_syntax noprefix
.global main
main:
mov eax, 0
ret
x86_64 Linux向けに書かれたGASアセンブリによるtrueコマンド
調査したところ、今回の希望を実現するのに最適な手段はCosmopolitanとBlinkに思えました。CosmopolitanはC言語で書かれたプログラムをWindowsを含む様々なプラットフォームで実行可能な単一の実行ファイルにする(!)ツールチェーンです。Cosmopolitan上ではLinux APIを使用可能です。Blinkはx86_64 Linux向けのプログラムをUnix系OS上で動作させる軽量仮想マシンで、機械語とシステムコールをエミュレートする一方、ファイルシステムなどのOSのリソースはゲストからそのまま見えます。ここで、C言語の代わりにx86_64 Linuxアセンブリを使うことで、アセンブリ言語で書かれたプログラムを他のプラットフォームでもそのまま動かせるのではないか。libcには依存することになるので、_startをエントリーポイントとし、自力でシステムコールを発行するわけではないですが、十分にアセンブリプログラミングを楽しめる環境なのではないかと思います。
注意点として、Cosmopolitanは推奨のcosmoccでコンパイルするとx86_64とARM64からなるファットバイナリを作成する(!)ので、代わりにx86_64-unknown-cosmo-ccを使う必要があります。それと、CosmopolitanやBlinkではフルセットのLinux APIやその他Linux由来のリソースが使えるわけではなく、主に各種ファイルAPIなどが利用可能です。ソケットも使いたいのですが、Blink上だとIPV6_V6ONLYなどいくつかの未実装な機能や、recvmsgなどいくつかのバグがある機能があり、名前解決ができないなど現状では利用が難しいようです。
Blink上で動かすプログラムはCosmopolitanで作成するのが推奨なようなので、今回Blinkを使う場合はCosmopolitanも併用しました。今回の構成では複数の動かし方が可能です。下記に示します。ここでAPEはCosmopolitanで作成した実行ファイルのことです。
こうして環境を用意した結果、ファイル入出力を伴う移植可能なUnixアプリケーションをアセンブリ言語で書くことができるようになりました。これで思う存分アセンブリ言語でプログラミングできます。しかしアセンブラはどうしましょうか。というのも、アセンブラごとにアセンブリ言語の構文や言語機能などが異なるため、どのアセンブラにするかを選ぶ必要があります。今回有力な選択肢はGAS (GNU Assembler)とNASM (Netwide Assembler)です。NASMはx86に特化したアセンブラで、豊富な言語機能を有します。GASは様々なアーキテクチャに対応した伝統的なアセンブラです。言語機能の優位性からか、FFmpegの一部などユーザー空間で書かれるアセンブリプログラムはNASMで書かれることも多いですが、RISC-Vなど他のアーキテクチャにも関心を持つ可能性がありそうなのと、よりUnix風のアセンブラを使いたいということで今回はGASにします。
しかし厄介なことに、x86 GASには更にAT&T構文とIntel構文があります。AT&T構文はUnixで伝統的に使われてきた構文ですが、命令の引数が逆になっているなどIntelのマニュアルで使われている構文と大きく異なります。Intel構文は文字通りIntelのマニュアルで使われている構文と似ていますが、あまり洗練されていないらしいのと、数年前まではラベル名とレジスタ名が区別できないという致命的な問題があったため、あまり使われてこなかったようです。しかし最近はラベル名とレジスタ名を区別できるようになったのと、Intelのマニュアルに近い構文で書けることから今回はIntel構文にします。
アセンブラとアセンブリの構文も決まれば、いよいよアセンブリでプログラミングできます。というわけで今回は基本的なtrue, cat, teeコマンドを書きました。ファイルの入出力が可能なことが分かります。x86_64 Linux、x86_64 Windows、ARM64 macOSで動作することを確認しました。移植可能ですね。これで今回の目的は達成できました。それではアセンブリプログラミングをお楽しみください。