情報科学若手の会で登壇してきた

先週末の三連休、第56回情報科学若手の会に、若手特別講演の枠でありがたくも呼んでいただけたので、参加して発表してきました!

発表で使用したスライドはこちらにおいておきます:

ちなみに開催期間中に人々と雑談をしていたら知ってびっくりしたのですが、なんとこの会は1968年7月から続く由緒正しきイベントで、インターネットの父として知られる村井純先生が1955年生まれであることを考えると、驚くほど歴史が長いです。

過去の開催報告も、1996年以降の分については公式ページに存在しており、参加者の名前を見ると、私が大学生の頃に退職された筧捷彦先生が幹事として参加されていたりと、なかなか衝撃的に歴史の長いイベントに気軽に足を踏み入れてしまったことに開催中に気づいてしまいました。沼ですね…。(いいぞ!)

時系列ざっくりまとめ

準備

ちょうど休みがほしい感じのタイミングだったので、労働からdetachして過ごす日々に、わくわくしながら準備をしていました。

なお準備ができているとは言っていない(スライドも荷物も、前日の段階では進捗0でした…急いでがんばりました)。

ちなみにドライヤーがないとしおりに記載されていたので、家からつよいドライヤーを持参しました。 (実際にはふつうの強さのドライヤーはデフォルトで用意されていたので、なくても大丈夫そうでした。来年以降の参考までに。)

初日

新幹線っていう高速回線でhikaliumは転送されました。

思ったより寒く、猛暑の夏から冬の始まりに急に飛ばされたような感じでした。

そして、問題はここから研修施設までの移動をどうするか。まあなんとかなるでしょ、の精神で来つつ、どうやら知り合いが車でくるらしいという情報を聞き、便乗させてもらおうと思っていたところ、なんと渋滞で大幅に遅れそうになっていたとのこと。そうか秋の三連休ですものね、皆様も旅をしたいと思っていらっしゃるのでしょう。仕方ありませんわ…(三連休開催のつらいところ)。

なので、同じく交通手段獲得RTAを走っていた参加者のお二人とエンカウントして、タクシーで現地へと向かうことにしました。

(ちょうどここで量子計算機関連の雑談ができて楽しかったです。)

若干遅刻しつつ参加したところ、「ブログを書くまでが #wakate2023」という、どこかで聞いたようなフレーズが耳に入ってきました。ということで、このブログを書いているわけです。(振り返り、大事なのでね!)

初日の講演は

  • GoでORMを自作する
  • スポンサーセッション : PFNを支えるストレージシステム
  • 招待講演 : 実用水準のプログラミング言語を個人規模でつくる

の三本立てとなっていました。

特に3つ目の、SATySFiの作者gfnさんによる講演は、首を縦に無限回振りたくなるほど、何かを自作する際に発生しがちな感情や思考の機微を的確に説明する講演で非常にためになりました。

ちなみにgfnさんとは帰りのタクシーで一緒になったため、フォントやPDF周りの闇についてわいわい盛り上がることができました。Type 1 fonts は闇が深い、覚えた!

夜は、人々で親睦を深めるために Estimathonという数字予想ゲームが開催され、非常に盛り上がりました。 まず、ルールを理解するところからして「結果が小さくなるほうが得点が高い」という部分が、普段の思考のバイアス(大きい方が得点が高い)に引っ張られて頭がぐるぐるしましたし、その後も、どの程度のレンジで値を特定すればいいのかの戦略など、単なるフェルミ推定的な予想を超えた面白さが満載で、めっちゃ楽しかったです。これ、会社の人々とかと、またやりたいなあと思いました。

なお、我々のチームは頭のネジが外れていたので、途中で結果が爆発的に大きくなってしまい、スコアボードに数値ではなく「とても大きい」と書かれるなどの実績を解除してしまいました…(でも楽しかった!!)

そのあとは、各自が持ち寄った大量のお菓子を囲みながら、人々とわいわい喋りつつ、私はスライド作成RTAをしていました…。

あと、次の日のLT大会に向けて準備していたmomokaさんの検証を手伝っていたら、なんか平日に見慣れた画面に突入してしまい労働の波動を感じるなどしてしまいました(オープンソースだからへーきへーき!!これは仕事じゃないもんね!!)

二日目

早起きして朝食を食べる超健康生活(なお午前3時までスライド作成やパケットキャプチャをしていた模様)をはじめつつ、本題の講演二日目が始まりました。

「Wasmを実行するunikernelとWasmコンパイラの開発」という発表でsaza_kuさんとainnoさんが提案していたエコシステムは、確かに上手く行けばめっちゃ熱い話だな…というか我々なんとかOSもこういう方向に進むべきなのでは!?!?という示唆を得られる、とてもわかりやすくためになる発表でした。これからの成果を楽しみにしています!

量子計算機関連の講演も複数あり、まさに冒頭の軽井沢駅から研修所までの道のりを共にしたお二人がそれぞれ発表した内容は、どちらも量子計算の基礎をわかりやすく説明してくれていて、とてもためになりました。

あと、量子コンピューターの世界におけるOSはどのようなものになるのか、ということについてもお二人とお話できて、将来考えてみる価値のある面白いテーマかもしれないな、と感じました。

また、海外では量子コンピューターを自作した人々がいる、という話もあったりと、自作欲をかきたてられるお話も色々と聞くことができました。私も元々量子計算機には少し興味があって、ある程度の知識は本から摂取していたので、それに加えて実際に専門として取り組まれている人々の話を聞くことができたのは、非常に楽しかったです。

そのあとは、私の発表ターンに続き、様々な面白いトピックでの発表が続きました。研究発表的な内容も、ざっくりと概観するような内容も、どちらも摂取できるのが、この会の良いところだなあ、と感じました。

また、そのあとのLTでも、面白い話がたくさんあったのですが、まだ未公開な話も多くあったので、とにかく楽しかったということだけを記しておくことにします。興味がある皆さんは、ぜひ次回の情報科学若手の会に参加を検討されてみてはいかがでしょうか!!!!!!!

そのほかのメモ

参加者のひとり、asu_paraさんの参加記事

hikaliumさんはセキュキャンなどを通してOSとか低レイヤーの人ということで知っていますし、講演もその通りに沿ったものです。ぼく個人としては、自身は概念のようなものと宣言されているにも関わらず、人間的な一面が見られるpostが多いのが趣深い人やなといつも思って、見てます。

と書かれていて、分かるな〜、となっています(まるで他人事のように2)

あとは、低レイヤと高レイヤの話についての視聴者の反応(めっちゃわかる)

他にも、同室の人々が私も含め、みんな引きこもりコンピューター大好きオタクムーブを発動していて面白かったです。(この界隈、性別の偏りが激しいので、相部屋(ひとり)になりがちの人々が久々に修学旅行的な雰囲気の部屋に放り込まれて、結果みんなおふとんの上で座り込んでパソコンカタカタする夜、みたいになっていて最高でした。)momokaさんのあやしいパケット検証を手伝ったり、ainnoさんのRustコードをレビューする会をしたり、たまにはこういうのも楽しいなあ!と感じました。

ちなみに:フィードバックに書き忘れたのですが、女子部屋が最も交通量の多い場所に配置されていたので、もしかすると交通量が少ない廊下の奥の方の部屋のほうがよいのではないか、という話が挙がっていました。来年以降の運営の参考になれば…。

あとは、狭い界隈の人々と物理世界で会えたのがよかったですね。大学の研究室の後輩から「先生が、たまには研究室に顔を出せって言ってましたよ!」と言われたりとか(なおその方はNVDIMMをやろうとしていたところ、Optaneがおしまいになったのでおじゃんになったらしい、かなしいね)、私が行こうと思っていた大学院の研究室に行く予定の学生の方とか(品川先生〜)、セキュキャンつながりの人々とか、とにかく、たくさんの方々と話すことができて、とても楽しかったです。

まとめ

久々のイベント登壇でしたが、面白いことをしているたくさんの方々と会って話せるお泊りイベントはとても楽しかったです。たまには日常を忘れて、こういうイベントに参加するのも、よいアイディアを得たり、今後の道筋を立てる助けになるような気がしました。また来年も参加したいなあ、という気持ちにさせてくれる、快適なイベントを運営してくださった、幹事の皆さまと、参加者のみなさま、ありがとうございました!またどこかでお会いしましょう〜!!

CPUを自作したりコンピューターアーキテクチャを理解するためにおすすめの本の一覧

hikaliumの独断と偏見で、積読は除いている。最近も結構新しい本が色々出ているので、それもいいかもしれないが、ある程度評価の定まった本を探したい場合に参考になれば。

ちなみに、hikaliumがセキュキャンでCPU自作を教えていたときのコードはここにある。参考にならないかもしれないが、おまけにどうぞ。

github.com

ディジタル回路設計とコンピュータアーキテクチャ

ハードウエア記述言語で実際にCPUをつくりながら、各アーキテクチャについても学べる良書。

MIPS版が広く知られているが、ARM版、RISC-V版も登場している。無印版はよくある技術書サイズだが、ARMとRISC-V版は大型本なので、そこらへんの好みとかも勘案するとよいかもしれない。

CPUの創り方

表紙がメイドさんだが、侮ることなかれ。(と私は中学生の時にクラスの人々の前で言いました。)

電子工作初心者の人々にもわかりやすく、回路素子の基本的な扱い方や、データシートの読み方も教えてくれる、実装寄りの良書。

74HCシリーズのICが手に入りにくいという現代的な事情はあるが、等価な回路を上の本などを参考にしつつVerilog-HDLで記述したりすれば、FPGAボードひとつでも等価なものを十分につくることができる。

RISC-V原典 オープンアーキテクチャのススメ

きれいなアーキテクチャで知られるRISC-Vの設計思想の背景や、どうしてその設計が選択されたのか、という思考の背後を知ることができる本。もう少し高いレベルからCPUのことを知りたかったり、RISC-Vについて知りたい場合には役立つ。

コンピュータアーキテクチャ 定量的アプローチ

通称ヘネパタ。大学でこの本を参考にキャッシュの特性を調べるグラフを描画する課題が出て、面白いなあと思った記憶がある。

めちゃくちゃ教科書的な内容なので、読むのは大変だし最初から最後まで読むタイプの本ではないが、持っておいて損はない。ペラペラめくったり目次をみて、興味のあるところや、参考になりそうなところを読む、辞書的な使い方をするのがよい。

作りながら学ぶコンピュータアーキテクチャ

上の方の本の翻訳にも携わっておられる、天野先生の著書。他の本では忘れられがちな、割り込み処理の実装方法が記述されているとネットで聞いて自分も購入した。

絶版本なので手に入れるのは少し大変。図書館などにあるかどうか、確認してみるのも手かもしれない。

上の分厚い辞書みたいな本たちにくらべたら、薄くて読みやすそうな見た目をしているので、一見の価値はある。

FPGAの原理と構成

いま気づいたがこれも天野先生の著書じゃん。おそらくCPU自作をはじめる最も手軽な方法はFPGAを用いることだが、じゃあFPGAって一体どうやって動いているの?というところの疑問を解決してくれる本。これも小さめ技術書によくあるサイズ感なので、電車の中でも読みやすいのでおすすめ。

Chromebookの開発者モードへの遷移をちょっと追う

某ブログに触発されて、公開情報からChromiumOSの気になるところを追う軽い記事でも書こうかなという気持ちに急になったので書いていく。正確さも何も保証しないし、公開情報しか載っていないのであしからず。公開情報であることを示すために出典へのリンクをいっぱい張ります。

Chromebookには開発者モードというunsecureなモードがあって、これを使うと自作OSをブートさせたり色々できて楽しい。それに入るためには、リカバリモードに入ってCtrl-Dを押してEnterを押すという作業が通常必要なわけだが、これで何が起きるのかちょっと追いかけてみよう。

まず、ChromebookBIOSでもUEFIでもないファームウエアを採用している。(一部例外はある。)これはverified bootとかvbootとか呼ばれていて、このあたりにいい感じの図がある。情報が最新の状態に更新されているかは謎だが…(斜め上に視線を動かす)

あと、ここのページにファームウエアまわりの情報が載っていて、こんなかっこいい一文が書いてある。

The Chrome OS firmware is always verified as signed by Google

いいはなし。Root of Trustってやつだね。

さて、このページの下の方に、H2C (UEFI-based), U-boot, Coreboot, とChromebookで使われていた歴代ファームウエアの名前が並び、それに続いてdepthchargeってやつが新し目のChromebookだと使われているぞ!と書いてある。へえ〜!

そこからこんなスライドへもリンクが飛んでいるが、なんと2014年である。まあつまるところ、多分近頃のChromebookはみんなdepthchargeを使ってるんじゃないかな?(適当)

さて、そんなわけで、depthchargeのコードはここにあるわけだが、まずREADMEがない。なるほど…?

ちなみに"depthcharge chromiumos"でググる無機質なGitilesというシステムのページに飛ばされるが、ここは検索機能とかがないので、便利なChromium Code Searchを使おう!超便利だよ!(いつもお世話になっているので頭が上がらない)

さて本題に戻って、とりあえずdepthcharge配下で"recovery"とかの単語で調べると、src/platform/depthcharge/src/vboot/ui.hというファイルがヒットする。

/* Screens. */
enum ui_screen {
    /* Wait screen for EC sync and AUXFW sync */
    UI_SCREEN_FIRMWARE_SYNC             = 0x100,
    /* Broken screen */
    UI_SCREEN_RECOVERY_BROKEN           = 0x110,
    /* Advanced options */
    UI_SCREEN_ADVANCED_OPTIONS          = 0x120,
    /* Language selection screen */
    UI_SCREEN_LANGUAGE_SELECT           = 0x130,
    /* Debug info */
    UI_SCREEN_DEBUG_INFO                = 0x140,
    /* Firmware log */
    UI_SCREEN_FIRMWARE_LOG              = 0x150,
    /* First recovery screen to select recovering from disk or phone */
    UI_SCREEN_RECOVERY_SELECT           = 0x200,
    /* Invalid recovery media inserted */
    UI_SCREEN_RECOVERY_INVALID          = 0x201,
    /* Confirm transition to developer mode */
    UI_SCREEN_RECOVERY_TO_DEV           = 0x202,
    /* Recovery using phone */
    UI_SCREEN_RECOVERY_PHONE_STEP1          = 0x210,
    UI_SCREEN_RECOVERY_PHONE_STEP2          = 0x211,
    /* Recovery using disk */
    UI_SCREEN_RECOVERY_DISK_STEP1           = 0x220,
    UI_SCREEN_RECOVERY_DISK_STEP2           = 0x221,
    UI_SCREEN_RECOVERY_DISK_STEP3           = 0x222,
    /* Developer mode screen */
    UI_SCREEN_DEVELOPER_MODE            = 0x300,

https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/depthcharge/src/vboot/ui.h;l=198-226;drc=3eda3d7bd3195f6d8ff59954e6b2967e42201bcb

UI_SCREEN_RECOVERY_TO_DEVとか、いかにもそれっぽい名前なので、今度はそれで検索すると、src/platform/depthcharge/src/vboot/ui.cというファイルが見つかる。まさに特定のキーを押したときに画面遷移するコードが書いてある!

 /* Manual recovery keyboard shortcuts */
    if (ui->key == UI_KEY_REC_TO_DEV ||
        (CONFIG(DETACHABLE) &&
         ui->key == UI_BUTTON_VOL_UP_DOWN_COMBO_PRESS)) {
        return ui_screen_change(ui, UI_SCREEN_RECOVERY_TO_DEV);
    }

https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/depthcharge/src/vboot/ui.c;l=39-44;drc=fcfa6efb8f221877e771a7bc382267a3ebcdee48

ちなみにここに書いてあるとおり、キーボードが外れるタイプのデバイス(DETACHABLE)だと、音量キーの同時押しでdevmodeに入れることがわかる。(もちろんドキュメントに書いてあるよ!)

さて、じゃあこの画面の遷移した先を見に行こう。

src/platform/depthcharge/src/vboot/ui/screens.c

static vb2_error_t recovery_to_dev_init(struct ui_context *ui)
{
    if (ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) {
        /* Notify the user that they are already in dev mode */
        UI_WARN("Already in dev mode\n");
        return set_ui_error_and_go_back(
            ui, UI_ERROR_DEV_MODE_ALREADY_ENABLED);
    }

    if (!CONFIG(PHYSICAL_PRESENCE_KEYBOARD) &&
        ui_is_physical_presence_pressed()) {
        UI_INFO("Physical presence button stuck?\n");
        return ui_screen_back(ui);
    }


    if (CONFIG(PHYSICAL_PRESENCE_KEYBOARD)) {
        ui->state->selected_item = RECOVERY_TO_DEV_ITEM_CONFIRM;
    } else {
        /*
         * Disable "Confirm" button for other physical presence types.
         */
        UI_SET_BIT(ui->state->hidden_item_mask,
               RECOVERY_TO_DEV_ITEM_CONFIRM);
        ui->state->selected_item = RECOVERY_TO_DEV_ITEM_CANCEL;
    }

    ui->physical_presence_button_pressed = 0;

    return VB2_SUCCESS;
}

static vb2_error_t recovery_to_dev_finalize(struct ui_context *ui)
{
    UI_INFO("Physical presence confirmed!\n");

    /* Validity check, should never happen. */
    if (ui->state->screen->id != UI_SCREEN_RECOVERY_TO_DEV ||
        (ui->ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ||
        ui->ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
        UI_ERROR("ERROR: Dev transition validity check failed\n");
        return VB2_SUCCESS;
    }

    UI_INFO("Enabling dev mode and rebooting...\n");

    if (vb2api_enable_developer_mode(ui->ctx) != VB2_SUCCESS) {
        UI_WARN("Failed to enable developer mode\n");
        return VB2_SUCCESS;
    }

    return VB2_REQUEST_REBOOT_EC_TO_RO;
}

https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/depthcharge/src/vboot/ui/screens.c;l=799-851;drc=85f76f13f623d3e4e3a01224a381abf755f1c1bb

vb2api_enable_developer_mode()ってやつがあやしいね!そして、それを呼んだあとは再起動してるみたい。depthchargeの中にはtest用のmockしかなかったので、ひとつ上のsrc/platformで検索をかけると、vboot_referenceで実装が見つかった。

vb2_error_t vb2api_enable_developer_mode(struct vb2_context *ctx)
{
    if (ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
        VB2_DEBUG("ERROR: Can only enable developer mode from manual "
              "recovery mode\n");
        return VB2_ERROR_API_ENABLE_DEV_NOT_ALLOWED;
    }

    uint32_t flags;

    VB2_DEBUG("Enabling developer mode...\n");

    flags = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS);
    flags |= VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE;
    vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS, flags);

    VB2_DEBUG("Mode change will take effect on next reboot\n");

    return VB2_SUCCESS;
}

https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/vboot_reference/firmware/2lib/2misc.c;l=433-452;drc=def2f5af7ac03eac3a4a79795275f1cf6cd7f827

VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE ってフラグがセットされるんだね〜

ということでこいつを調べると、フラグの定義も見つかった。

/* Flags for firmware space */
enum vb2_secdata_firmware_flags {
    /*
     * Last boot was developer mode.  TPM ownership is cleared when
     * transitioning to/from developer mode.  Set/cleared by
     * vb2_check_dev_switch().
     */
    VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER = (1 << 0),

    /*
     * Virtual developer mode switch is on.  Set/cleared by the
     * keyboard-controlled dev screens in recovery mode.  Cleared by
     * vb2_check_dev_switch().
     */
    VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE = (1 << 1),
};

https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/vboot_reference/firmware/2lib/include/2secdata.h;l=25-40;drc=6a33a0fca3e4f5cd9c3b3fd4ac0b5b8c7ffc018e

コメントいわく、最後の起動時の状態も記録しておいて、もしnormalからdevに遷移していたら、TPMのownershipをクリアするって書いてあるね!まあ要するに、normalのときに保存されたユーザーの秘密情報がもれないように、なんでもありのdevmodeに遷移するときはデータを消し飛ばすってことなんでしょう。(実際devmodeに移行するとなんかの消去が走っている画面になる。)

次回はその消去の画面のところを見に行きましょう!(オープンソース部分であることを祈りましょう…)

おやすみなさい!

VRM as a Code - またはコードによりあなたのアバターを定義する方法について

下書きのまましばらく放置されたのでインターネットの海に放流

目標

hikaliumのアバターをコードで定義して、GUIを用いることなく服を着せ替えたりパラメーターを操作するなどできるようにしたい

先行研究

背景情報

VRM形式

人型アバターで最近主流になりつつあるファイルフォーマット。glTF形式をベースにしている。

glTF形式

調査

VRM/glTFを理解する

bin領域のデータのエンディアン

Little Endianにしなければならない(MUST)

All buffer data defined in this specification (i.e., geometry attributes, geometry indices, sparse accessor data, animation inputs and outputs, inverse bind matrices) MUST use little endian byte order.

UV map

https://docs.blender.org/manual/ja/dev/addons/import_export/scene_gltf2.html#uv-mapping

KHR_texture_transform

Animation

three-vrm example の humanoidAnimation のデモがデバッグに使えそう

実装

github.com

とりあえずVRMをglTFとして読み込んだら、テクスチャ付きでprimitive単位に分割して再出力することはできた。

また気力が湧いたら続きをやろう。

2022年の振り返り

例年通り、今年あったことの概要をまとめておく。

1月

3Dプリンターをお買い上げしてた(今年だったんだ…)

ちなみにブログ記事も書いた

hikalium.hatenablog.jp

2月

3Dプリンタメンガーのスポンジを印刷してた(筆記用具は商品には含まれません)

そしてhikaliumとして初の社のイベント登壇をした

そしてもう一台3Dプリンタが増えた(錯乱)

戦争とかが始まったのもこのあたりらしい(世も末ですな)

あまりにも眠れなさすぎる現状を歌にするなどした(かっとしてつい…聴かなくていいですよ!!!)

3月

情報処理学会のOS研究会で社の仮想ブースを回すなどしていた

新しいお友達もお迎え(コアスイッチとして現在も頑張ってくれています)

おうちNOC業の様子

車輪を再発明した(車輪ではなく車(輪がついた物体の進入を防ぐ)輪)

技術的な方法でTwitterの閲覧時間を削減する試みを開始(まあまあ役に立っており、今もつかっている)

4月

社の公式ブログでお仕事に関する記事を書いた

Redox を読む会を始めた(次回をスケジュールするのを忘れていました…来年やります!)

セキュキャンのCコンパイラゼミが、元受講生の講師を迎えて再始動することが決定(本当にめでたかった!)

ZOZOMATを試すなど(足、印刷しなきゃ…)

ちなみにイエベ春らしいです(ZOZOGLASS曰く)

社のイベントで人々に自作OSはいいぞ!をするなどした

スライドはここ

そして Asahi Lina さんとコラボした!(Linaさん、つよつよなので尊敬している)

Linaさんのチャンネルにお邪魔して、私が自作OS語りをしてくる回もありました!

5月

kurenaif魔女のチャンネルにKOBA789氏と一緒にお邪魔して、DirtyPipe ( CVE-2022-0847 )を完全理解した

そして弊チャンネル初のSuperThanks受領(ありがとうございます!)

思い立ったのでosdev-jpのcode of conductを制定するなどした

2月に公開した歌の解説記事を書いた(解説の必要な歌って、なに?)

3Dプリンタがあるなら、ということで3Dスキャナに入門した(むずかしい)

USB Type-C の電気的な側面の理解を試みるなどした(少し理解した)

そして!はじめての!オフィスでの!夕食!(入社2年半以来初の快挙)

分解衝動の発散

6月

計算資源が追加された

ふるさと納税メロンを摂取(ひとりで2個まるごと食べるのはなかなかの難易度だった…超美味しかったけど…)

ついにToDoリストアプリをつくるというToDoに着手(現在メンテ中で落ちてる)

7月

Rust for Linuxでお遊びする配信をするなどした

[大人の]科学を大人買いするなど

そしてRP2040を飛ばす

そして破壊衝動の発散(ではなく異音の原因を除去した)

ちゃんと原因特定している図

他の物体も分解(分解ばっかりしてるな)

Optaneはもうおしまいです、わたしは悲しいよ…(終わらせないぞ!!!)

活版印刷入門

8月

夏はやっぱりセキュキャン!自作OS!だよね!!

あと電源は大事です。電源は大事なので。

インターンの人々の優しい言葉に救われました。本当にありがとうございます!!!

そして深淵を覗く

9月

はじめての入院入門!

ハードウェアアップデートのためしばらくオフラインに

生還したので辞世の句にはならなかった歌ってみた動画

2週間の入院後なんとか帰宅したら家は無事ではなかった話(またエアコンから水かよ(自力でなんとかしました))

リコリコに影響されて「力」を手に入れるなど(もちろん合法な力です)

ikaliumです(入院中に発売されていたスプラ3に入門)

10月

低レイヤーガール、チャンネル登録者数4000人突破!!(ありがとうございます!)

二次元セルオートマトンを一発実装する THE FIRST CODE をやってみたら、しょうもないコンパイルエラーで終了した件(ちゃんと後でデバッグしました!)

そして新しい物理アバターのmetrics exporter(丸くてかわいいのでお気に入りです)

久々に物理世界のイベントに参加して自己肯定感が向上した図(hikaliumは優しい皆様に救われています)

11月

情報公開(伏線回収)

やさしい世界

そしてDNSの浸透

3年間お預けだった、リアルワールド開催のSTEP Class 8に参加してきた!(mallocチャレンジの参加者の何名かに賞を差し上げました!)

テプラSR5900Pのプロトコルを解析して、CLIから印刷できるツールを実装した

物質、いいよね(用法用量を守って正しく使いましょう)

12月

ポケモンSV、はじめました!

Open Source Summit 聴講(社の人間としては初の物理カンファレンス参加)

WebUSBに入門した(自作OSでドライバ実装したときの大変さに比べたらマジで拍子抜けするほど簡単でびっくりした、みんなもっとこれで遊ぼう!)

/sys/hikalium/tracing を生やした(デバッグ用なのでノイズが多いですが興味あればどうぞ)

hikalium.hatenadiary.jp

胸に刻んでおきたい言葉(手前味噌ですが)

hikaliumのロゴの背後の深層心理はこれでは?という発見

no (クリスマス) spanning-tree

大掃除

著者近影

総括

振り返ってみれば、想像以上にたくさんのことをやっていたことに気づいた。もっと自信を持って欲しい > 自分

ついに情報公開も果たしたが、誰もが何事もなく受け止めてくれて、人々の優しさを再確認した。これからは、以前にも増して、自分の「好き」をもっと大切にしてゆきたいと思う。(みなさんもぜひ、そうしてくださいね!)

おしごとに関しては、外から見える範囲のCommunity contributionをたくさんできたのは素晴らしかったし、これからも続けてゆきたいが、もっと強くなって技術的な成果のほうもさらに挙げてゆきたい。問題は、時間が足りないというところだが…(生き残りたい!!!)

あと、もう少し精神を安定させてゆきたいですね。もっと、ゆっくり、やっていこう。活動を抑え気味にしているつもりでも、これだけのことをできているのだから。

今年お世話になった皆様におかれましては、本当にありがとうございました。

来年以降も引き続き精進してまいりますので、hikaliumをどうかよろしくお願いいたします!!!

Rustで自作OSをしているときのデバッグ例 - syscall 命令と仲良くなりたい!後編

この記事は自作OS Advent Calendar 2022の18日目の記事です。前回のあらすじはこちら:

hikalium.hatenablog.jp

例外を吐いてるのはどこのどいつだぁ?

さて、前回の状況をおさらいすると、

  • 0x200fd0の命令がアドレス0x2000000000にアクセスしようとしてページフォルト(0xe)を発生させた。
  • ページフォルトを処理している最中に一般保護例外(0xd)が発生し、一般保護例外を処理中にもさらに一般保護例外が発生することで、順次スタックが食い潰されていった。
  • 例外ハンドラのスタックが枯渇し、0x3de4bed8にアクセスしようとした段階でページフォルトが発生した。これにより、ダブルフォルトが発生した。
  • ダブルフォルトハンドラ内で再度ページフォルトが発生した結果、トリプルフォルトとなってリセットがかかった。

というわけで、まずは最初のきっかけである 0x200fd0とかにある命令がどこのどいつかを特定することにしましょう。

まず、アプリのロード状況を確認します。OSのログを見ると、

Executable found: Elf { name: hello, data: @0x000000003e2d9000 } 
This ELF seems to be executable!
phdr_start: 64, phdr_entry_size: 56, num_of_phdr_entry: 5
type  : (rwx) = (101)
offset: 0x0000000000000000
vaddr : 0x0000000000200000
fsize : 0x0000000000001B64
vsize : 0x0000000000001B64
align : 0x0000000000001000

type  : (rwx) = (110)
offset: 0x0000000000001B68
vaddr : 0x0000000000202B68
fsize : 0x00000000000001C8
vsize : 0x00000000000001C8
align : 0x0000000000001000

load_region_start = 0x0000000000200000
load_region_end = 0x0000000000203000
load_region @ 0x000000003d233000
dst_range: 0x0000000000000000 - 0x0000000000001B64
dst_range: 0x0000000000002B68 - 0x0000000000002D30
run the code!
entry_point = 0x000000003d2332f0

というふうになっています。これはどういうことかというと…図示したほうがはやそうなので、お絵描きしました。

現在のアプリケーションのロードの様子

現在は手抜きをするために、アプリケーションとOSのメモリ空間を分けていないので、アプリの希望通りのメモリレイアウトではなく、相対位置を維持したまま、異なるアドレスにCodeとDataの両方をロードしているのでした。

さて、例外を発生させたメモリアドレス(0x200fd0)は、このロードされたコード(0x3d233000-0x3d234b64)の中…にありませんね…あれ?

…と思って状況をよくよく確認すると、どうもこのアドレスは、アプリがもともと予期していた、コード領域がロードされるべきアドレス範囲(0x200000-0x201b64)の中にあるようです…つまり、アプリケーションが正しく再配置可能ではなかった、もしくはローダーが仕事をサボっているせいで、正しく再配置できなかった、のいずれかの可能性であると考えられます。ちょっとアプリのバイナリのほうを確認してみましょう。

$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -A 3 200fd0
0000000000200fd0 <<core::fmt::Arguments as core::fmt::Display>::fmt>:
  200fd0:       48 83 ec 38             sub    $0x38,%rsp
  200fd4:       48 8b 06                mov    (%rsi),%rax
  200fd7:       48 8b 76 08             mov    0x8(%rsi),%rsi
  200fdb:       48 8b 4f 28             mov    0x28(%rdi),%rcx

objdumpに-Cオプションをつけると、シンボルをデマングル(人間が読みやすい形式に変換)してくれるみたいで、Rustのシンボル名にも対応してるみたいです。ありがとうbinutils!

というわけでですね、この200fd0は、明らかにDisplay::fmtを呼び出しているコードで利用されていて、我々のprintln!のコードパスで通っていそうな雰囲気がひしひしと伝わってきます。

さて、これを呼び出している側のコードはどうなっているのでしょうか?rip相対のcallではこのようなことは起こり得ないので、レジスタ間接でのcallがDisplay::fmtへ制御を移そうとしているはずです。なので、レジスタ間接callを使っている人々をリストアップしましょう。

$ `brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep -E -e 'call.*\*' -e ' <.*>:$' | grep 'call.*\*' -B 1
0000000000200860 <<core::panic::panic_info::PanicInfo as core::fmt::Display>::fmt>:
  200886:       41 ff 56 18             call   *0x18(%r14)
  2008bf:       ff 50 18                call   *0x18(%rax)
--
0000000000201030 <core::fmt::write>:
  2010b7:       ff 50 18                call   *0x18(%rax)
  201177:       ff 54 08 08             call   *0x8(%rax,%rcx,1)
  2011d8:       ff 50 18                call   *0x18(%rax)
  2011e9:       42 ff 54 35 08          call   *0x8(%rbp,%r14,1)
  20122f:       ff 51 18                call   *0x18(%rcx)
0000000000201250 <core::fmt::Formatter::pad_integral>:
  2013f1:       ff 55 20                call   *0x20(%rbp)
  20150a:       41 ff 55 20             call   *0x20(%r13)
  201557:       ff 55 18                call   *0x18(%rbp)
  201582:       41 ff 55 18             call   *0x18(%r13)
  2015aa:       41 ff 55 20             call   *0x20(%r13)
00000000002015d0 <core::fmt::Formatter::pad_integral::write_prefix>:
  2015f1:       41 ff 54 24 20          call   *0x20(%r12)
0000000000201630 <core::fmt::Formatter::pad>:
  2018ea:       41 ff 56 20             call   *0x20(%r14)
  20190d:       41 ff 56 18             call   *0x18(%r14)
  20193f:       41 ff 56 20             call   *0x20(%r14)

まあ…たぶんcore::fmt::writeですよね…きっとそうだ…

`brew --prefix binutils`/bin/objdump -d -C target/x86_64-unknown-elf/release/hello | grep '<core::fmt::write>:' -A 155 | grep -z -E -e 'call' --color='always'

qemu/target/i386/cpu.h

$ cat qemu_debug.log | grep hikalium | head -n 3
hikalium: raise_interrupt2: is_int == 0 intno = 14 eip=0x000000000000200FD0 error_code = 0x000000000000000000
hikalium: rsp: 00000000003E286BE0
hikalium: ret_to: 00000000003D2341EE
$ cat com2.log | grep -a -e entry_point
entry_point = 0x000000003d2332f0
$ make objdump_hello | grep '<entry>:'
00000000002002f0 <entry>:
$ ./scripts/debug/inspect_app_crash.sh 
0000000000201030 <core::fmt::write>:
  2011e9:       42 ff 54 35 08          call   *0x8(%rbp,%r14,1)
  2011ee:       84 c0                   test   %al,%al

.data.rel.roがあやしい!

https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/specialsections.html

Chromium Docs - Native Relocations

Relative relocations and RELR | MaskRay

$ `brew --prefix binutils`/bin/objcopy -O binary --only-section=.data.rel.ro generated/bin/hello data.bin
[00:34:16]
[local] hikalium@t05.z01.hikalium.com: ~/repo/wasabi  
$ hexdump -C data.bin 
00000000  58 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |X. .............|
00000010  58 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |X. .............|
00000020  58 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |X. .............|
00000030  e0 04 20 00 00 00 00 00  08 00 00 00 00 00 00 00  |. .............|
00000040  08 00 00 00 00 00 00 00  20 06 20 00 00 00 00 00  |........ . .....|
00000050  f0 04 20 00 00 00 00 00  c0 05 20 00 00 00 00 00  |. ...... .....|
00000060  60 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |`. .............|
00000070  60 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |`. .............|
00000080  61 01 20 00 00 00 00 00  08 00 00 00 00 00 00 00  |a. .............|
00000090  60 01 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |`. .............|
000000a0  e0 04 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000000b0  01 00 00 00 00 00 00 00  70 07 20 00 00 00 00 00  |........p. .....|
000000c0  b0 03 20 00 00 00 00 00  80 04 20 00 00 00 00 00  |. ....... .....|
000000d0  e0 04 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000000e0  01 00 00 00 00 00 00 00  70 19 20 00 00 00 00 00  |........p. .....|
000000f0  94 01 20 00 00 00 00 00  13 00 00 00 00 00 00 00  |.. .............|
00000100  37 00 00 00 05 00 00 00  94 01 20 00 00 00 00 00  |7......... .....|
00000110  13 00 00 00 00 00 00 00  53 00 00 00 05 00 00 00  |........S.......|
00000120  db 01 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |. .............|
00000130  dc 01 20 00 00 00 00 00  03 00 00 00 00 00 00 00  |. .............|
00000140  a8 02 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
00000150  a8 02 20 00 00 00 00 00  01 00 00 00 00 00 00 00  |. .............|
*
00000170  40 1a 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |@. .............|
00000180  01 00 00 00 00 00 00 00  50 08 20 00 00 00 00 00  |........P. .....|
00000190  b8 02 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |. .............|
000001a0  b8 02 20 00 00 00 00 00  02 00 00 00 00 00 00 00  |. .............|
000001b0

https://uclibc.org/docs/elf-64-gen.pdf

Hardening ELF binaries using Relocation Read-Only (RELRO)

https://maskray.me/blog/2021-08-29-all-about-global-offset-table

Airs – Ian Lance Taylor » Linker relro

cnlelema.github.io

時間切れ

というわけで追いかけていたら.data.rel.roの沼にはまったので今日はここまで。明日もやるかも。おやすみなさい…。

Rustで自作OSをしているときのデバッグ例 - syscall 命令と仲良くなりたい!前編

この記事は自作OS Advent Calendar 2022の17日目の記事です。他の記事も是非お楽しみください!(そして書ける方はぜひ参加してみてください!!)

前回(?)までのあらすじ

hikaliumは自作OS上で動くアプリからsyscall命令を使ってシステムコールを呼べるようにしようと頑張っていたが、なぜか発生するトリプルフォルトによりQEMUが再起動してしまい、3時間のデバッグの末力尽きてしまった。一体なぜ例外が発生するのか、その謎を解くため、我々は数日の休息をとったのち、バイナリの森へと旅立った…。

前回(という名の配信アーカイブ):

www.youtube.com

状況を整理しよう

バイナリの森は危険だ。無闇に動きまわっては、x86の沼に足をとられて命を落としかねない。まずは我々の向かっていた先と、これまでに得た情報をまとめることにしよう。

どこへ向かっていたのか

我々のひとまずの目標は、自作OS上で実行されるアプリケーションから、Rustのprintln!()マクロを介して文字列を出力することであった。Rustはとてもよくできていて、coreクレートの中に、C言語でいうsprintfに相当する、fmtモジュールが存在しており、既存のOSの支援が期待できないno_std環境でも変数の中身などを容易にかつ美しく表示する関数およびマクロを容易に実装することができる。そう、我々はもう闇のようなlibc++やnewlibに頼らなくてもよくなったのだ。10年前のように、自前でsnprintfもどきを実装するまでもない。世界は日増しによくなりつつある。

ではprintln!マクロを実装するにあたり、自作OSが提供しなければならない機能は一体何かというと、それは「文字列を表示する」という機能である。というのも、coreクレートにはOSに依存するような機能は含められないため、「文字を画面に出す」などといった、物理世界とのインタラクション、つまりはハードウエアの制御などのOSが支配している領域の操作はcoreクレートの外で自前で用意してやらなければならない。core::fmtは、フォーマット文字列と引数をもとに、メモリ上に「表示するべき文字列」を生成してはくれるが、それを表示するのは我々の責務なのだ。

というわけで、我々はprint_stringというシステムコールを用意した。これは、メモリ上に存在する文字列へのポインタと、文字列の長さを指定すると、OSがそれを画面上に表示してくれる、というものである。実は、このシステムコール自体はどうも動作しているようで、実際にアプリケーションからprint_stringシステムコールを介して固定の文字列を出力してみた場合は、想定通りに文字列が出力されていたことが確認できている。

それならば、あとはprint_stringに対して、core::fmtによって生成された文字列を渡してあげればよいのではないか、我々はそう踏んでいたのだ。

なにがあったのか

…しかし、その考えはあまりにも浅はかだった。実際に、以下のようにprint_stringを置き換えてみたところ、なんとOSが再起動してしまったのだ。

#![no_std]
#![no_main]

use noli::*;

fn main() -> i64 {
    //sys_print("hello!");
    println!("{}", 42);
    return -42;
}

entry_point!(main);

どうも、何らかの理由でCPUの例外が発生し、トリプルフォルトに至って再起動してしまう、というところまではわかったのだが、我々はそこで一旦力尽きてしまったのだった…。

まずは何が起きているのかを理解しよう

現状は先ほど伝えた通りだ。次は、我々がどちらに向かえばよいのか、それを考えることにしよう。そのためには、我々がいま直面している現象を正しく理解することが必要不可欠だ。まずは、具体的にどのような例外でトリプルフォルトに至っているのか、それを確認することにしよう。

QEMUには、例外が発生した際にログを出力する機能が存在している。これはデフォルトでオフになっているが、-d というフラグを介して制御することが可能だ。

$ qemu-system-x86_64 -d ?
Log items (comma separated):
out_asm         show generated host assembly code for each compiled TB
in_asm          show target assembly code for each compiled TB
op              show micro ops for each compiled TB
op_opt          show micro ops after optimization
op_ind          show micro ops before indirect lowering
int             show interrupts/exceptions in short format
exec            show trace before each executed TB (lots of logs)
cpu             show CPU registers before entering a TB (lots of logs)
fpu             include FPU registers in the 'cpu' logging
mmu             log MMU-related activities
pcall           x86 only: show protected mode far calls/returns/exceptions
cpu_reset       show CPU state before CPU resets
unimp           log unimplemented functionality
guest_errors    log when the guest OS does something invalid (eg accessing a
non-existent register)
page            dump pages at beginning of user mode emulation
nochain         do not chain compiled TBs so that "exec" and "cpu" show
complete traces
plugin          output from TCG plugins

strace          log every user-mode syscall, its input, and its result
tid             open a separate log file per thread; filename must contain '%d'
trace:PATTERN   enable trace events

Use "-d trace:help" to get a list of trace events.

おっと、言い忘れていたが、我々は以下のバージョンのQEMUを自前ビルドして利用している。一応伝えておこう。

$ qemu-system-x86_64 --version
QEMU emulator version 7.2.0 (v7.2.0-dirty)
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers

さて、本筋に戻るとしようか。というわけで、以下のようなオプションをQEMUに追加すれば、例外に関するログが、カレントディレクトリの qemu_debug.log というファイルに出力されるはずである。(出力先のファイルは-Dオプションで指定している。) また、 --no-reboot をつけておくと、トリプルフォルトでCPUにリセットがかかった際に、再起動するのではなくQEMUを終了することができるようになる。再起動のループを楽しみたい気分であるなら話は別だが、これも設定しておくとデバッグが楽になるだろう。

-d int,cpu_reset -D qemu_debug.log --no-reboot

さて、これでもう一回自作OSを起動してみる。アプリは自動で起動するように設定してあるから、クラッシュしてすぐQEMUが落ちるはずだ。

…予想通り落ちた。ログを確認しよう。

$ cat qemu_debug.log | grep -A 1 -e check_exception -e Triple
check_exception old: 0xffffffff new 0xe
   159: v=0e e=0000 i=0 cpl=0 IP=0008:0000000000200fd0 pc=0000000000200fd0 SP=0010:000000003e286be0 CR2=0000002000000000
--
check_exception old: 0xffffffff new 0xd
   160: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003e286928 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xffffffff new 0xd
   161: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003e286668 env->regs[R_EAX]=0000002000000000
--
...
--
check_exception old: 0xffffffff new 0xd
  6458: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4c1a8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xffffffff new 0xd
  6459: v=0d e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0xd new 0xe
  6460: v=0e e=0002 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 CR2=000000003de4bed8
--
check_exception old: 0xe new 0xe
  6461: v=08 e=0000 i=0 cpl=0 IP=0008:000000003e290f91 pc=000000003e290f91 SP=0010:000000003de4bee8 env->regs[R_EAX]=0000002000000000
--
check_exception old: 0x8 new 0xe
Triple fault

おお、結構な量のログが出ている。はて、一体何が起きているのだろうか…。

例外コードを順に追っていくと、以下のような流れで例外が起きていることがわかる。

  • ページフォルト(0xe)が0x200fd0の命令を実行中に発生した。ページフォルトの要因となったアドレスは CR2=0x2000000000 である。
  • 一般保護例外(0xd)が0x3e290f91の命令を実行中に発生した。
  • 同じ例外が次々と発生し、スタックポインタが0x3e286928から0x3de4bee8まで変化した。
  • 最終的に、スタックポインタと同じアドレスであるCR2=0x3de4bed8 にアクセスしようとして、再度ページフォルト(0xe)が発生した。
  • ここでさらにページフォルト(0xe)が発生し、これによってダブルフォルト(0x8)が発生した。
  • ダブルフォルト(0x8)の発生中にさらにページフォルト(0xe)が発生し、最終的にトリプルフォルトとなってリセットがかかった。

つまるところ、

  • 最初のきっかけは、0x200fd0の命令がアドレス0x2000000000にアクセスしようとしてページフォルト(0xe)を発生させたことにある。
  • そのページフォルトを処理している最中に一般保護例外(0xd)が発生し、一般保護例外を処理中にもさらに一般保護例外が発生することで、順次スタックが食い潰されていった。
  • 例外ハンドラのスタックが枯渇し、0x3de4bed8にアクセスしようとした段階でページフォルトが発生した。これにより、ダブルフォルトが発生した。
  • ダブルフォルトハンドラも同一のスタックを使用していたため、再度ページフォルトが発生し、トリプルフォルトとなってリセットがかかった。

ということのようだ。 …例外を正しく処理できないOSでごめんなさい…(仕方ないよ、完璧なOSなんて存在しないもの。直していきましょう!)

さて、ここらへんで日付変更線が近づいてきたようだ。続きはまた明日…(睡眠は大事ですよ!おやすみなさい!)