世界を動かす技術を、日本語で。

Linuxはインタプリタです

概要

  • 本記事は、前回のシリーズの補足として、単独で理解可能な内容
  • curlで取得したスクリプトの正体を解析し、安全性や動作を詳細に解説
  • スクリプトがLinuxカーネルとinitrdを再帰的にkexecする仕組みを説明
  • initrdやELFの解釈、自己複製(Quine)プログラムとの関係性を考察
  • Linuxカーネルや実行形式の「解釈者」としての側面を深堀り

curlで取得した謎のスクリプトの正体

  • コマンド例: curl https://astrid.tech/rkx.gz | gunzip | sudo sh
    • 何をしているか分からず不安を感じるコマンド
  • ファイルの正体を調査
    • gunzip後のrkxは POSIXシェルスクリプト
    • 中身は約20MBの巨大なbase64データ
  • スクリプトの動作概要
    • root権限の確認
    • kexec, base64, cpio の有無をチェック
    • base64データをデコードしcpioアーカイブ「r」を生成
    • cpioから「k」ファイル(カーネルイメージ)を抽出
    • kexecで「k」(カーネル)と「r」(ramdisk)をロード・実行

スクリプトの実体と再帰的kexec

  • 「r.cpio」は initramfs として機能
    • /bin, /init, k(カーネルイメージ)を含む
  • /initスクリプトの内容
    • /procをマウント
    • 自身のファイル群をcpioで新たな/rにまとめる
    • kexecで新たなカーネルとramdiskを再起動的に実行
  • この仕組みの特徴
    • 再帰的にkexecを呼び出すLinuxディストリビューション
    • 実行のたびにカーネルとinitrdを置き換え続ける
    • まるで「自分の尻尾を追いかける」ような再帰

initrdとQuine(自己複製プログラム)

  • Quineの定義: 自分自身を出力する自己複製プログラム
  • /initの最後にcat /rを追加すると、 initrd Quine となる
    • RAM上のファイルのみを扱うため、実質I/Oは発生しない
  • 読者への問い
    • 「最小のinitrd Quineはどれくらい小さくできるか?」

Linuxカーネル・ELF・インタプリタの解釈連鎖

  • Linuxカーネルは「initrdのインタプリタ」として機能
  • スクリプトの実行
    • シェバン(#!/bin/sh等)により、 インタプリタに渡されて解釈実行
    • ELFバイナリにはインタプリタとしてld.soが指定されている場合がある
  • ELFの実行プロセス
    • カーネルがld.soを起動
    • ld.soがELFを読み込み、必要なライブラリをロードして実行
    • 静的リンクの場合はカーネルが直接解釈
  • 解釈の連鎖
    • /bin/shはスクリプトを解釈
    • ld.soは/bin/sh(や他のELF)を解釈
    • カーネルはld.so(静的リンク)やELF自体を解釈
  • 実行形式でないファイルを実行しようとした場合
    • 例:cpioアーカイブを直接実行→「exec format error」

Linuxの実行モデルと「プログラムとしてのinitrd」

  • initrdは「プログラム」、カーネルはその「インタプリタ」
  • kexecを用いた再帰的なOS置換のユニークさ
  • プログラミング言語や実行形式の「解釈される立場」の再考
  • シェルスクリプト、ELF、initrdそれぞれの「解釈」構造の対比

このように、curlで取得したスクリプトは「再帰的に自分自身をkexecするLinux OS」という、シンプルながらも奥深い仕組みを持つものでした。initrdや実行形式の「自己複製性」や「解釈者」の連鎖といった、OSやプログラミングの根本的な仕組みを考えるきっかけとなる内容です。

Hackerたちの意見

まあ、Linuxってのは、実行可能でプログラム可能なタスクをこなすための、ちょっと一般的なインターフェースみたいなもんだよね。これをやるためにWindowsを使うこともできるけど、個人的にはLinuxの方がそのタスクには向いてると思う。ただ、Windowsのグラフィカルユーザーインターフェースはちょっと良いかもしれない。あのウィンドウのインターフェースは本当にイライラするけど、GNOMEもKDEも同じくらいウザい。最近はfluxboxやicewmを使ってることが多いけど、たまに気分転換でxfceやmate-desktopも使うかな。でも、全体的には「ハードコアデスクトップ時代」は終わった気がする。速くて効率的でシンプルなものが欲しい。大体の作業はコマンドラインでやってて、ちょっとウェブブラウジングしたりエディタでコードやテキストを書いたりしてる。ほとんど95%はそんな感じ。

「Windowsが良いのはグラフィカルユーザーインターフェースだけだと思う。」いや、そうでもないよ。GNOMEやKDEについては君が正しいけど、Windowsはもっとひどい。マイクロソフトの狂った迷路から逃げられないからね。正直、デスクトップインターフェースのゼロックスの血統にはあまり興味がない。mpx/muxの系譜が好きなんだ。9wm、cwm、dwm。エンゲルバートに近くて、全体的に見ても良いと思う。

速くて効率的でシンプルなものが欲しい。Sway + フットで、各ワークスペースを好みに合わせて設定できるのはかなりいいね。デスクトップはないけど、使い方にはすごく合ってる(私もそう)。ウィンドウのフォーカスを自分が一番楽に使えるキーにバインドしてみて。

シリーズの初めの方から。「最初にこれをやった理由は、VPSをプレメイドのディスクイメージから起動するために、Contaboに月1.50ドル余分に払いたくなかったから。」50時間かけて月1.50ドルを節約するのと、「すべてのエンジニアは月25万ドルをトークンに使うべきだ」というのの間には、ちょうどいいバランスがあると思う。ホストの従業員も食べていかなきゃならないし、月1.50ドルすら払えないなら、プロとは言えないし、実際にプロが払っているインフラにただ乗っかっているだけだよね。もっと極端なこともできるし、GitHubページやVercelのサブドメインでただやり過ごしている開発者もたくさんいる。だから、少なくともVPSを持っていることで、その競争の中で一歩リードできるけど、月1.50ドルを節約しようとするのは厳しい状況だと思う。その時点で技術的なスキルがボトルネックになるとは思えないし、むしろ何か社会的な作業が必要なんじゃないかな。curlでdoomを動かすことにこだわるのは、経済的に厳しい状況ではあまり生産的な時間の使い方じゃないと思う。これを書いているのは、私がその状況にいるからだけど、もしかしたら考えすぎかもしれない。

うーん、ちょっと考えすぎじゃない?払えないっていうより、払いたくなかったって感じかな。それを避けるために使った解決策を考えるうちに、Linuxについてもっと学ぶための大きな穴にハマっちゃったんだよね。自分でバカな問題を解決しながら。

「ホストの従業員」っていう言葉、いいね。LLMの寄生虫が私たちを使って自らを具現化し、特異点に再生産するみたいな。

「何か社会的な作業が必要なんじゃないかな。curlでdoomを動かすことにこだわるのは、経済的に厳しい状況ではあまり生産的な時間の使い方じゃない。」それは問題かもしれないけど、ただ自分の特別な興味に没頭して楽しんでいる人もいるよね。ADHDの私にとって、特別な興味に関わることはメンタルヘルスを保つために必要不可欠だから、すごく良い時間の使い方だと思ってる。

それ、私がやってたことみたいだな… 子供の頃、月5ユーロのVPSはかなりの出費で、時々10GBのrootfsを母のWindowsノートパソコンにダウンロードして、インスタンスを終了させて、十分なお金ができたら再構築してた。最終的には、Terminal IDEっていうアプリが動く古いKindleを手に入れて、Linuxシェルとbusyboxやgccみたいな基本的なプログラムが使えるようになった。Spartacus Rex、もし見てたら、私のキャリアを可能にしてくれてありがとう。

もし1.50ドル/月すら払えないなら、プロとは言えないし、プロが支えている本物のインフラの上でただ流されているだけだね。これは変な主張だな。誰かが何かをしてお金をもらっているかどうかがプロかどうかを決めることであって、他の誰かにいくら払っているかは関係ないんだよね。(それが重要なことで、アメリカ人の変な言い回しで使われる「プロフェッショナル」とは全然違う。)

安いハックを「プロじゃない」と呼ぶのは本質を見失ってる。意外と便利なトリックは、自動で全部にお金を払うのをやめたときにしか現れないんだよね。

著者は確かにそう書いてるね。でも、明らかにジョークだよ。本当の理由は次の段落に書いてあるんだ。> 面白いトリックだと思ったし、永遠のcurl | sh論争をネタにした面白いシットポストだよ。これについてブログ記事を書けるし、自分でやる方法を教えることもできる。一千語で、私も何かを学び、君も何かを学び、私はネットポイントをゲットする、ウィンウィンだね。

すべてのOSって、カーネル権限を持つ機械語のインタープリターじゃない?

これはCPIOファイル用のインタープリターだよ。

いや、OSのソフトウェアは各命令を個別に読み取って何をするか決めるわけじゃないよ。ハードウェア(CPU)に渡して、その命令を実行させるんだ。

OSはシステムリソースを使うためのインターフェースだよ。最近は、システムリソースを安全に使うのが複雑だから、これを実現するためのソフトウェアやインターフェースの集合体になってる。CPUは機械語を解釈するけど、OSはCPUに何を実行するか指示することがある(設計によるけど)。

この文章は誤解が多くて読むのが辛かった。cpioアーカイブはファイルシステムじゃない。著者はinitramfsを使っていて、これはtmpfsに基づいてる。Linuxはcpioをtmpfsに展開できる。ファイルやディレクトリのアーカイブはそれ自体がプログラムじゃない。見た目が似てるからって、同じだとは限らないんだよ。バイナリプログラムはCPUで実行されるから、インタープリターが関わっている場合はハードウェア環境に隠れている。それはOSカーネルの範囲外だよ。ファイルシステムにシェルスクリプトがあって、それを実行する場合、スクリプトを解釈するシェルも提供しないといけない。著者はこの詳細を省いて、カーネルとシェルプログラムを混同している。Linuxはinitramfsやramdiskのサポートなしで簡単にコンパイルできるし、ファイルシステムにあるユーザランドを起動して実行できる。「Linux initrdインタープリター」は私の脳を痛める。そんな風には動かないよ。追記:もっと読み進めるべきだった。まだ説明の仕方が逆だと思うけど。

少なくともAIのゴミじゃないからね!

ファイルとディレクトリのアーカイブ自体はプログラムじゃないよね。でも、ELFファイルも単体ではプログラムじゃないって言えるよね。実際、いくつかのELFファイルはエントリーポイントがない動的ライブラリで、別のプログラムに接続されない限り、実行可能とは言えないんだ。もし、いくつかのELFファイルが実行可能で、いくつかはそうじゃないって認められるなら、いくつかのCPIOも実行可能で、いくつかはそうじゃないってことも認められるよね。ld.soがELFファイルをRAMに展開してエントリーポイントを実行するのと、LinuxカーネルがinitramfsをRAMに展開してエントリーポイントを実行するのは、何が違うの?

cpioの中のinitが解釈されるプログラムで、残りのcpioはその解釈されたプログラムのためのメモリだよ。

バイナリプログラムはCPUで実行されるけど、プログラムファイルはセクションを持つアーカイブで、通常はそのうちの一つがプログラムで、他はすべてメタデータだよ。CPUはプログラムファイルを理解することができないんだ。Linuxはプログラムが実行される条件を設定しなきゃいけなくて、少なくともプログラムカウンタがあるアドレス空間を確立して、そのアドレスにジャンプする必要があるんだ。その方法についての命令はELF実行可能ファイルのメタデータセクションにあるよ。

man ld.so: ...(この場合、動的リンカーにコマンドラインオプションを渡すことはできず、ELFの場合、プログラムの.interpセクションに格納された動的リンカーが実行される)ELFセクションの名前に注目してみて。

チューリングのシータコンビネータ

すべてがインタープリターなの?

うん、コンパイラを除いてね。

面白いのは、そのアナロジーが正しいかどうかじゃなくて、「実行」がどれだけ環境に依存しているかを浮き彫りにしているところだよね。