パッチ patchぱっち
今でもバイナリファイルを修正するのに、差分データを使って更新するのに「パッチを当てる」という言い方が残っていますが、元々は紙テープをツギハギしてデータを編集する時に使う言葉だったようです。
パッチ
まあ、このように穴を塞いだり開けたり、テープなので、切ったり貼ったりしてコードを修正していたのですね。貼る時にはテープが折れたり千切れないようにできればのりしろも欲しいです。のりしろには貼る相手のコードが変わらないようにDELコードで埋めたりするのです。
紙テープがすべての始まり
紙カードであれば修正を加えるのに該当部分のみを入れ替えれば済むので、こんな苦労はしないで済んだのですが(その代わりブチ撒けて順序が崩れると一大事)、もう少し経つと、入力とは無関係に、出来上がったバイナリを直接、修正する時にこの言葉を使うようになりました。
ちなみにテレックスではこの技を使っていましたけど、自分がコードを書く時代にはもうテープもカードも(ギリギリ)ありませんでしたよ(まさに廃棄物置き場に積まれていました)。
マシン語の場合、特に「リロケータブルではない」書き方をしていれば、命令を追加すると飛び先のアドレスを全て変更しなければなりません。そんなことをすれば再度アセンブルをして新たなバイナリを作る必要があります。そんなことをせずに、命令を追加したい部分から一旦、使われていないアドレスにJMPして、そこに命令を追加して元のアドレスにしれっと戻すのです。
この使われていないアドレスというのが、なかなか絶妙で、サブルーチンの開始アドレスをわかりやすい(キリの良い)アドレスにするためにもサブルーチンの最後にあるリターン命令の後ろに隙間を作っておくなんていうことは日常的にやっていました。まあ文化によっては最初から「パッチ領域」と名前をつけて NOP を並べておくなんていうこともあったようです。
パッチを当てた後、次のアップデートでちゃんとアセンブルし直したコードにすれば良さそうなものですが、既に決まったサブルーチンのアドレスを呼び出すプログラムが他にあったりするので、もうそのまま使うしかありません。そして再度、パッチが当てられ…と、ソースコードのないスパゲティなコードが蔓延るわけです。
自分で経験した良く使ったパッチは少し違っていて、コードにバグがあった時にプログラムをすべて再コンパイルすると、それなりに大きなプログラムだったので、たとえmakeを使っていたとしても数時間かかることがありました。その間、デバッグを続けることが出来ないのは痛いので、デバッガでバイナリを直接、編集して、そのコードを吐き出すようにソースコードを修正するのです。デバッグ中なので最適化されていないバイナリなので「こういうコードになるはずだ」というのは何とか書くことはできます(それでもそれなりの熟練が必要な技ですが)。そしてバックグラウンドで再コンパイルを始めつつ、デバッグを継続することができるんですね。まあ正確に同じにならないこともあるのですが、一応、中間ファイルまでは作ってみて確認はしておくんで、まず大丈夫です。今ならリソースが豊かなので、大人しく再コンパイルした方が良いのですが、ひとつバグを見つけるたびに再コンパイルしていたら納期に間に合わないという環境は、別に珍しくもなんともありませんでしたね。
その後はパッチと言えば、プログラムを修正する時に、バイナリ全体をダウンロードしてとやるとサイズが大きすぎるので、特にネット環境が貧弱な時代には、差分データをダウンロードして修正を行うことが一般的になり、patchというコマンドも整備されました。もっともこのコマンドはソースコードの修正に使われるもので、バイナリを直接編集するのはセキュリティ的にも危険なので、そのためのプログラムを作るのが普通なようです(そうすれば証明書とかもつけられる)。
patch
やはりパッチというのは、なかなか危険なもので、プログラム(場合によるとデータベースのデータなども)をダクトテープで直すようなものなので、使う誘惑にかられることはよくあるのですが、褒められるものではありません。
とはいえ太古のパッチ技術に頼らざる得ないこともまだ起こります。走っているプログラムを走ったまま修正せざる得ないときなどに、デバッガでプロセスを開き、コード領域を書き換えて事なきを得ることが無いわけではありません。もちろんセキュリティ的に危ないことをするわけですからrootでやるわけですし、間違っては大変なことになりますし、正確に何を行ったかを残す必要(そして場合によれば何台もやる)があるので再現可能なようにデバッガコマンドのスクリプトで実行する羽目になるわけです。まあいわゆる緊急対応ということで止血のためのやむない処置ではあるのですが。
これを経験してからは、もう2度とやらないで済むように、シグナルで必要なプロセスを立ち上げ直したり、パラメタファイルを読み直して縮退運転に入ったり、小細工をするようになりました。
危なくて仕方がないので採用はしなかったのですが、パッチファイルからコードをデータとして変数に読み込み、ここに飛び込むことでパッチが当てられる機能を検討したこともあります。まあ普通のローダが使えないので、いろいろ制約もあるのですが。他にもOSには実はいろいろなフック機能もあったりするので、随分と調べました。こんなことばかりしていると、ウィルスの作り方の勉強にもなってしまいますし、だいたいにおいてシステムのセキュリティに跳ねられないための戦いにもなります。ただ公式に認められている手順もあるので、必要なら使う価値はあります。
もう仕事のコードは書いていないので、こんな心配をする必要は無いのですが、過去のシステムはリプレイスもままならない状態で使われることも多く、アップデートすら困難なシステムがたくさんあります。にも関わらず外部要因で修正せざる得ない時にパッチが活躍するという悲しい現場があるのも確かで、残念ながら廃れない技術なようです。いやはや。
この記事は、AyumiKatayama さんの以下の記事に触発されて書きました。
私が初めてパッチをあてた日
ヘッダ画像は、以下のものを使わせていただきました。
https://commons.wikimedia.org/wiki/File:Harvard_Mark_I_program_tape.agr.jpg
ArnoldReinhold - 投稿者自身による著作物, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=34872964による