1 ポイント 投稿者 GN⁺ 2023-10-16 | 1件のコメント | WhatsAppで共有
  • 30年前のプロプライエタリなスプレッドシート Lotus 1-2-3 が、偶然復旧された資料とバイナリハックを経て、発売当時には存在していなかった Linux 上でネイティブ動作するようになった
  • 1990年代のBBSバックアップから、失われたと思われていた LPL アドイン SDK と Lotus 1-2-3 for UNIX が見つかり、サンプルプラグインのビルドと UNIX バイナリ解析が可能になった
  • UNIX 配布物には圧縮された Intel 80386 COFF オブジェクト 123.o が含まれており、約20,000個のシンボルとデバッグ情報が内部構造を明らかにした
  • 移植は COFF を ELF に変換するだけでは終わらず、Linux と UNIX の システムコール・構造体 ABI の違いcoffsyrup とラッパー関数で回避する必要があった
  • 現在のポートはクラッシュせず動作し、100% 使用可能だが、一部の問題や端末ドライバの移植が残っており、見た目では DOS エミュレーション版のほうがまだ優れている

30年前のプロプライエタリアプリを Linux に移植した出発点

  • Lotus 1-2-3 は30年前の abandonware として残っていたが、いくつかの発見とハックによって Linux ネイティブポート が可能になった
  • 移植対象は古典的な Lotus 1-2-3 R3 であり、Lotus 1-2-3 自体は 2002 年までリリースがあった
  • このプロジェクトは、現代のシステムで Lotus 1-2-3 を正しく動作させるための lotusdrv ドライバ保守作業から始まった
    • ドライバ API はリバースエンジニアリングで解明され、既存ドライバは現代のシステムでもうまく動作する

失われた LPL SDK と BBS バックアップ

  • Lotus 1-2-3 はプラグイン、つまり アドイン(add-in) で拡張できるよう設計されていたが、アドインは LPL という特殊な言語で書かなければならなかった
  • LPL コンパイラと SDK は失われており、Lotus は SDK を無料配布せず 395ドル で販売していた
  • 1990年代の BBS システムのテープバックアップを保管していた元 sysop を通じて、warez 形式の SDK が復旧された
    • 復旧された ADK は 123DADK.ZIP から取得できる
    • サンプル LPL プログラムは modern.pl として公開されている
  • 同じ BBS バックアップには、広く失われたと考えられていた Lotus 1-2-3 for UNIX の warez コピーも含まれていた

Lotus 1-2-3 for UNIX ディスクイメージの解析

  • UNIX 版は 123UNIX1.TD0 から 123UNIX5.TD0 までの TD0 ファイル 一式として残っていた
  • TD0 は1980年代の古い圧縮ディスクイメージ形式であることが確認され、samdisk を使って raw ディスクイメージに変換した
  • 変換したイメージのうち最初のものは tar archive、残りは pre-SVR4 または odc 形式の ASCII cpio archive だった
  • ディスクイメージ一式は Internet Archive にアップロードされた
  • 展開すると lotus/123.v10 配下に実行ファイル、ヘルプ、キーマップ、プリンタ・ビデオドライバデータ、サンプルファイル、マニュアルなどが現れた
    • 全体構成は 35個のディレクトリと338個のファイル 規模だった

決定的な手がかりとなった 123.o

  • sysV386/lib ディレクトリで 123.o.z_1123.o.z_2 に分割された大きなファイルが見つかった
  • 2つの断片を結合して展開すると、123.oIntel 80386 COFF object file であることが確認された
    • stripped されていないオブジェクトファイル
    • 5つのセクション
    • シンボルオフセット 0x1efbdc
    • 19,755個のシンボル

      • optional header size 28
      • このファイルには private symbol とデバッグ情報が含まれており、Lotus 1-2-3 の内部構造を理解するうえで大きな助けになった
      • alt.folklore.computers で得られた回答では、このファイルは誤って含まれたものではないという見方だった
      • 1990年代初頭の UNIX では dlopen() が広く使われていなかったため、拡張機能と再リンクするための partially linked オブジェクト だった可能性が高い

デバッグ情報が明かした内部構造

  • UNIX 版は DOS 版の 直接的なソースポート に近く、ほとんどが同じ方式で動作していた
  • 含まれていたデバッグ情報は、Lotus 1-2-3 の内部に関するさまざまな疑問を解決してくれた
  • グラフ出力を改善するため、ドライバで ラスタライザ(rasterizer) にフックをかけたかったが、文書だけでは動作を理解しにくかった
  • デバッグ情報により、ラスタライザが小さな バイトコードプログラム を動的に生成し、グラフィックエンジンがそれを解釈していることが判明した
  • opcode が分かったことで、raster.c でそれを逆アセンブルして変更し、出力改善に活用できるようになった

COFF から ELF への変換

  • objcopy は COFF オブジェクトファイルを Linux で使う ELF 形式へ変換できる
  • objcopy -I coff-i386 -O elf32-i386 123.o 123elf.o コマンドで 123.o123elf.o に変換した
  • 変換結果は ELF 32-bit LSB relocatable, Intel 803386, version 1 (SYSV), not stripped と確認された
  • objdump によれば、元のオブジェクトファイルのコンパイル時刻は 1990年9月8日 06:23:50 で、Linux の最初のバージョンより前だった

Linux と UNIX の ABI 差異への対応

  • 最初の問題は、Linux と UNIX が互換性のある システムコールインターフェース を使っていない点だった
  • UNIX 版は lcall7 インターフェースを使用しており、たとえば open()call 0x7:0x0 形式の callgate を呼び出していた
  • Linux ではこの callgate がサポートされておらず、そのまま実行するとクラッシュした
    • Linux にはかつて lcall7lcall27 の互換サポートがあったが、現在はない
  • 目標はこうした呼び出しを除去し、すべての呼び出しを glibc 経由にルーティングすることだった

再配置と coffsyrup

  • 単純に objcopy --strip-symbol open でシンボルを削除しようとしたが、openrelocation に使われているため objcopy に拒否された
  • relocatable object file は、どのアドレスにロードされても動作するようシンボル参照と再配置情報を含んでいる
  • シンボルを削除するには、そのシンボルを参照する 再配置情報 も一緒に修正しなければならない
  • そのために小さなツール coffsyrup を作成した
    • objcopy が拒否する COFF シンボルも削除できる
    • 例として open シンボルと関連する relocation を見つけて処理した

バイナリ互換ではない UNIX 関数群

  • 標準 UNIX 関数のかなりの部分は ソース互換 ではあっても バイナリ互換 ではない
  • 代表例は struct stat
    • 同じ C コードでも、複数の UNIX 系でコンパイルできる
    • しかしコンパイル済みオブジェクトファイルは struct stat のサイズや st_size オフセットの違いにより別のシステムでは壊れる可能性がある
  • 対応が必要な主な関数は stat(), times(), uname(), fcntl(), ioctl() などだった
  • アプローチは、objcopy でシンボル名を変え、coffsyrup で undefined にしたうえで、Linux 構造体を UNIX 構造体へ変換する ラッパー関数 を書くことだった
  • termios はとくに大きな難所で、細かな動作が UNIX システムごとに微妙に異なり互換性がなく、デバッグが難しかった

ライセンスチェックと実行結果

  • いくつものハックのあと、プログラムはクラッシュせず起動したが、ライセンス がなければ動作しなかった
  • exit() にブレークポイントを置いて確認した結果、有効なライセンスを確認する内部シンボルは lic_init() だった
  • IDA でコードを調べたところ、ライセンスチェックは LICENSE.000 ファイルを探し、その中の有効期限、ユーザー名、システム名がシステムの報告値と一致するかを確認する方式だった
  • 最終的に Lotus 1-2-3 は新しい OS である Linux へ移植された
  • 現在の状態は以下の通り
    • クラッシュせず動作する
    • 100% 使用可能

      • まだいくつかの問題を詰める必要がある
      • 端末ドライバの移植が残っている
      • 現時点ではエミュレーション上で動く DOS 版のほうが見た目は良いが、修正は可能
      • 移植コードは github で公開されている

1件のコメント

 
GN⁺ 2023-10-16
Hacker News の意見
  • 古い BBS システムのテープバックアップから SDK の warez コピーを復元し、サンプルプラグインまでビルドしたというのは信じがたいほどです
    Why History Needs Software Piracyを思い出します

    • 現在販売されているシンセサイザーや Eurorack 機器などのマニュアル、ファームウェア更新、ドライバ、ユーティリティを集めて、長期保存アーカイブを作りたいと思っていました
      こうした機器は数十年後も残っているハードウェアなので、メーカーごとに個人のクラウドへ保管する程度を超えて、コミュニティ全体で使える中央集権的で長く存続するリポジトリがあれば、公共的価値は大きいです
      毎年アーカイブを配布したり、100年ほど持つ物理メディアとして archival Blu-ray を販売したりする方法も考えていました
      そうすれば、ARM ベースの DSP Eurorack モジュールの壊れたファームウェアを復旧するときに必要な最後の希少な .bin ファイルを、誰かが持っている確率は 1 に近づくはずです
  • 父が古い Windows の入った予備ノートPCに重要な Lotus 1-2-3 ファイルを大量に保存していて、たまに開く必要があったため、その状態はかなり不安でした
    最近、LibreOffice が Lotus ファイルを ODF/ODS に、しかもコマンドラインからでも変換できることを知り、簡単な bash スクリプトで全部変換しました
    変換したファイルを Collabora 付きの Nextcloud に一括アップロードし、父にアカウントと簡単な使い方を教えたところ、今ではオンラインでファイルを見られ、サーバーの通常のバックアップルーチンにも含まれています
    OP のようなデータ考古学者と LibreOffice コントリビューターには大いに感謝しています

    • そこまで古いのにまだ重要なファイルとは、どんな種類のファイルなのかかなり気になります
  • 7歳のとき Nintendo を買ってほしいと頼んだら、父が代わりに 286 PC と Lotus 1-2-3、Indiana Jones のテキストアドベンチャーを買ってくれた記憶がよみがえります

    • Boston の Computer Museum で見た LISP Machine が格好よく見えて、それが欲しいと言ったのですが、結局 Commodore 64 をもらいました
      そのため最初の言語は LISP ではなく BASIC になり、人生のあらゆる失敗をこの悲劇のせいにするつもりで、反論は受け付けません
    • NES を買ってくれとせがみましたが、父が「こっちのほうがいい、後でわかる」と言って C64 を買ってくれ、結局人生が変わりました
    • BBC Micro が欲しかったのですが ZX Spectrum をもらい、結果的にはプログラミングに興味を持たせるにはずっと良かったです
      Spectrum がすでに下火になりつつあった時期でもそうでした
    • Apple IIgs と Leisure Suit Larry をもらいましたが、私も年上の兄も、起動時に出る年齢確認の質問の答えを安定して当てられませんでした
  • 似たような作業をしていますが、プログラムの一部をまずオブジェクトファイルに変換する段階を追加しています
    そのため、既存の残っているオブジェクトファイルだけに縛られませんが、動作させるにはある程度のリバースエンジニアリングが必要です
    Atari Jaguar のリンカーである aln で概念実証を作り、Linux x86 の a.out 実行ファイルを ELF オブジェクトファイルに分離したうえで、ABI の違いを回避して ELF x86 実行ファイルとして再リンクしました
    https://forums.atariage.com/topic/354341-porting-the-original-atari-jaguar-sdk-to-elf/

  • いろいろな面で興味深かったです
    ソースコードも元のコンパイラと言語もなしに、UNIX バイナリから Linux バイナリへ移植する過程が見事に見えましたし、こういうのこそ HN らしい内容です
    2023年になっても Usenet が単に rar ファイルの流通以外の用途で使われていることも思い出させてくれます
    インターネット普及前の BBS 文化が、熱意と優れたシェアウェア配布を一般に広めていた過去も思い起こさせます

    • BBS 精神が戻ってきているように思います
      Twitter をはじめとするソーシャルメディアが崩れていく流れを見ると、なおさらですし、年長の Z 世代は80〜90年代レトロにはまって、モデムのネゴシエーション音を着信音としてダウンロードしたりもしています
      23歳の甥も最近そうしていましたし、American Horror Story や Stranger Things のような作品がそのノスタルジーをさらに養っています
      十分長く待てばすべてがまた流行する、という古い言葉は正しいということです
  • 昨年2022年5月にも良い議論がありました
    https://news.ycombinator.com/item?id=31455968

  • この素晴らしい記事からリンクされていた https://scenelist.org/ は、今朝まさに必要としていたノスタルジー爆弾でした
    思い出旅行にとても良く、強くおすすめします

  • 関連リンクです
    Lotus 1-2-3 for Linux - https://news.ycombinator.com/item?id=35807639 - 2023年5月、コメント73件
    Lotus 1-2-3 - https://news.ycombinator.com/item?id=35872758 - 2023年5月、コメント117件
    Lotus 1-2-3 For Linux - https://news.ycombinator.com/item?id=31455968 - 2022年5月、コメント83件
    Lotus 1-2-3 arbitrary resolution - https://news.ycombinator.com/item?id=26316637 - 2021年3月、コメント123件

  • 昨年少し使ってみたせいで、1-2-3 のことをすっかり忘れていました
    今でも Linux で 123 バイナリが問題なく動いており、はるか昔に消えたソフトウェアがそのまま動作するというのは、かなり大した成果です

  • ずっとこういうものが欲しいと思っていました。スプレッドシートでフォントや図をあまり使わないので、ターミナルベースのスプレッドシートがぴったりです
    ついに出たのはうれしいですし、Mac と Windows にも移植されるといいですね

    • 同じウェブサイトには、さまざまなターミナル Linux スプレッドシートプログラムのレビューもあります
      https://lock.cmpxchg8b.com/spreadsheet.html
    • Emacs には昔から、スプレッドシートモードである SimpleEmacsSpreadsheet がありました
      使ったことはありませんが、確認してみる価値はあります
      また Emacs の Org-Mode は際限なく掘り下げられるツールで、org-mode の表セルを Emacs の Calc 機能と一緒にスプレッドシートのように使う方法もあります
      https://www.youtube.com/watch?v=15aDIvb5LJs
      かなり強力そうで、Emacs らしくターミナルでもウィンドウ環境でも実行できます
    • すでに sc があります: https://www.unix.com/man-page/linux/1/sc/