私が初めてパッチをあてた日
パッチ。
それは、冬の寒い季節になるとおっちゃん達がズボンの下にはく下着のことである。
というのではなくって!
プログラミングにおいてパッチというのは、Wikipediaによると次のようにある。
よくわからん。
「バグ修正や機能変更を行う」というのは目的なのでこれを取っ払ってしまうと次のようになる。
「プログラムの一部分を更新するためのデータのこと」
プログラムそのものではないということか。
「更新するためのデータ」といわれてもやっぱりよくわかない。なので、私の大いに勝手な理解で説明しよう。
私の理解では、プログラムのパッチというのは、
機械語を修正する
ことである。
私が初めてパッチをあてたのはおそらく30年くらい前になると思う(正確に30年前だったかどうかなど、さっぱり覚えていない)。まだ、社会人1年目くらいだったか。作業したのは客先。トラブルの原因がわからず客先に出向いて調べることになった。
当時のソフトウェアを開発する環境というのは、今とは比べものにならない。設計文書は手書き。ミニコンのUnixでviを使ってソースコードをエディットしコンパイルした。全ソースコードをコンパイルすると2時間や3時間かかる。バックアップは磁気テープ。入社配属されて間もないころ、先輩に「バックアップしに行くけど見てみる?」と誘われて、もちろんNoであるはずもなく先輩の後ろからついていった。その時、先輩が持っていたのが大きな磁気テープである。私の知っていた磁気テープというと音楽カセットテープくらいであったが、それとは比較にならないくらいに大きい。フライパンサイズである。バックアップするのも時間がかかる。半日だったか1日だったか。とにかく、なにかにつけて物のサイズは大きく時間がかかる作業ばかりだった。
客先に行って調査するにしても客先でコンパイルすることはできない。会社でコンパイルして送ってもらうこともできない。デジタルでデータ伝送できるようになったのはその数年後だ。だから、プログラムを修正したいとなると機械語を直接編集するしかない。別に趣味で機械語を触っていたわけではないのだ。
私はまだパッチをあてたことはなかった。だから、正確には
「私が初めて先輩にパッチをあててもらった日」
というのが正しい。
先輩「どこをどう直したい?」
私「えーと、ここにこういうコードを2行挿入したいんです。」
先輩「挿入ね。マップある?」
私「あ、はい、これです」
先輩「空き領域あるかな・・・ここ、空いてるよね。」
私「空いてます。使ってません。」
先輩「じゃ、ここ使おうか」
プログラムをマシン語で挿入する場合、後ろのコードの配置をずらすわけにはいかない。関数コールは全てアドレスに変換されているので、関数のアドレスが後ろにずれてしまうと呼び出せなくなるからだ。そもそも、データを後ろにずらすという操作もない。だから、
空き領域を見つけてそこに一旦ジャンプし
ジャンプするために潰したコードと
挿入したいコードを書いて
元のところにジャンプしてもどる
というコードを書く。
その先輩は、それこそパッチの達人のような人だった。ちょっとしたコードであれば、マシン語が頭に入っている。いちいちニーモニック表を見ることもない。
ちなみに、一番使用頻度の高い命令は「NOP」命令である。「No OPeration」の略で、その名の通り何もしない。何故そんなものが必要なのか。パッチをあてる場合、元のマシン語を置き換えなければならないのであるが、マシン語によってサイズが異なる(最近は、どの命令も同じサイズというCPUもある)。新しいマシン語がうまい具合にピッタリはまるとは限らない。足りないときは前後数ステップを潰して置き換える。逆に余った時にどうするのか。ここで、NOP命令の登場である。余った領域をNOPで埋めるのである。Intelの8086あたりだとマシン語で「0x90」だったように思う。とにかく、「9」と「0」のキーを連打したものだ。
ついでに「マップ」というのはメモリのマップである。プログラムをコンパイルしてマシン語に変換するとマップも出力される。マップファイルの出力はオプションだったかもしれないが、今でも必須で出力している。プログラムがメモリのどこに配置されて、データがどこに配置されるのかがこれでわかる。当然、使用されていないメモリ空間もわかるということなる。更にはまた、例外発生時にスタックフレームから例外発生時のプログラムカウンタを取り出すことができれば、マップからソースコードを特定することもできる。当時デバッグするときはこのマップとアセンブルリストは必須であった。
さて、メモリのどこを使うのかを決めて修正コードが決まれば、後はマシン語で打ち込む。デバッガを使うことが多いがこれがそれなりに大きくて、かつ重い。タワー型のデスクトップくらいではなかったか。しかもその中にモニタまで入っているのである。図体が大きくてもモニタの画面サイズは小さい。大きめのスマホくらいの画面だったように思う。覗き込まないと読めない。鼻先を引っ付けるようにしてモニタを覗き込みながらキーボードを打つんである。
で、それらを先輩がやってくれる。
「確認してみて」
そう言われて覗き込んだモニタには、打ち込んだデータを逆アセンブルしたコードが表示されている。ジャンプ先のアドレス、戻ってくるアドレス、アクセスするデータのアドレス、データのアクセス単位、等々を確認する。
「大丈夫です。ありがとうございました。」
これが、私が初めて接したパッチである。
この時以降、私もパッチを愛用するようになる。ついでに言うと、パッチをあてるのは必ずしも客先作業だけではない。職場で試験デバッグするときにも頻繁に使った。コンパイルし直すのも時間がかかり、プログラムをROMに焼くのも時間がかかる。デバッガにロードするのだって一苦労だ。だから、ちょっと試したいというような時にはパッチをあてる。
もう、10年、いやそれ以上か、パッチをあてたことはない。最近ではC言語を修正してコンパイルし直した方が速いからだ。今やROMに焼くのだってあっという間である。実にありがたいことだ。
あの頃、文字通り、一から十まで教えてもらうことばかりだった。だが、マニュアルや手順書があったという記憶がない。先輩がやっていることを見て聞いて理解し覚えた。正直のところ、エンジニアはマニュアルや手順書を作るのが苦手なように思う。仮に手順書を作ったとして、後から読んでもよくわからなかったりする。
デバッガの使い方を見て聞きデバッガが何かを知る。
時にはそれでデバッグが何かを知る。
ROMに紫外線を照射して消去する。
ROMがリードオンリーであることを実感する。
なんだか、ほとんど、体を動かして学んでいった感じである。手順書があっても書いていないことも少なくない。経験者には是非いろいろ聞いてみてほしい。
「ここは、こうした方がいいよ」
そう言われたこと以上のものが、きっとたくさんあるから。
古い話になってしまった。
プログラミングの歴史の一端(隙間?)としてこういうことを記録するのもいいかなと思った次第である。
今の若い人達も是非残しておいて。あっという間に古くなるから(苦笑)。