質問: 「ゼロからのOS自作入門」にg++での挑戦を試みています。 環境はUbuntu20.04.02、g++9.3.0です。 現在、USBからのマウス操作を、ポーリングによる検知から割り込みによる検知に変えるという作業の途中です。(購読されている方は、7章です)ここで割り込みハンドラの記述のため、以下のような関数を作成することになりました。 ``` //main.cpp __attribute__((interrupt)) void IntHandlerXHCI(InterruptFrame* frame) { main_queue->Push(Message{Message::kInterruptXHCI}); NotifyEndOfInterrupt(); } ``` これをコンパイルすると、以下のようなエラーが出ました。 ``` main.cpp:86:42: sorry, unimplemeted: SSE instructions aren't allowed in an interrupt service routine ``` 調べたところ、どうやら割り込みを記述する際には汎用レジスタのみを利用するようにコンパイラに指示する必要があるため、オプションで-mgeneral-regs-onlyを付加しなければならないらしいと知り、今度はこれを付加してコンパイルを試みたところ、次のようなエラーが出ました。 ``` In file included from /home/(私の名前)/osbook/devenv/x86_64-elf/include/c++/v1/numeric:143, from main.cpp:6: /home/(私の名前)/osbook/devenv/x86_64-elf/include/c++/v1/limits: In static member function ‘static constexpr std::__1::__libcpp_numeric_limits::type std::__1::__libcpp_numeric_limits::min()’: /home/(私の名前)/osbook/devenv/x86_64-elf/include/c++/v1/limits:313:77: error: SSE register return with SSE disabled 313 | _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR type min() _NOEXCEPT {return __FLT_MIN__;} | ^ /home/(私の名前)/osbook/devenv/x86_64-elf/include/c++/v1/limits: In static member function ‘static constexpr std::__1::__libcpp_numeric_limits::type std::__1::__libcpp_numeric_limits::min()’: /home/(私の名前)/osbook/devenv/x86_64-elf/include/c++/v1/limits:405:77: error: x87 register return with x87 disabled 405 | _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR type min() _NOEXCEPT {return __LDBL_MIN__;} | ``` main.cppではnumericをインクルードしているため、それが原因であろうということは察しが付きましたが、試しにのインクルードを外してみたところ、その他のファイルで多数使用されているfunctional、array、optionalなども同様のエラーの発生源となっていました。 しかし、著者が想定環境としているclang(及びclang++)ではコンパイルが成功し、何もエラーが出ません。 そこでお聞きしたいのが、 1. 結局、著者のソースコードに概ね従う限り、g++でコンパイルを行うことは不可能なのでしょうか?それとも、何か付加すべきオプションがあるのでしょうか? 2. `-mgeneral-regs-only` を付加するべきであるという私の理解は正しいのでしょうか?著者の用意したMakefileの設定やビルド用の環境変数の設定には、これに対応すると思しきオプションは見当たりません。clangではこのようなオプションを付加しなくても、割り込み `__attribute__((interrupt))` を記述すれば、SSEレジスタの使用が抑制されるということなのでしょうか? ご存知の方いらっしゃれば、ご教授願いたく存じます。
回答: この辺の話は Application Binary Interface (ABI) 仕様の理解(ならびに著者と読者間での合意)がないと説明できないし、オイラはその本もっていませんのでなんともいえないのですが まず ABI の解説から 関数の引数や返却値ってのは限りなく抽象的なものであるのに対し CPU のレジスタやスタックってのは物理的なものであって、 その両者をどう結び付けるとか、関数の呼び出し前後でレジスタやスタックがどう変化する/させるとか、その他の諸々、関数を呼ぶ側・呼ばれる側で守るべき規則の仕様を ABI といいます。 x86 calling conventions とか The Definitive Guide to Linux System Calls とか これを守れば1つのプログラムの中に A 社のコンパイラと B 社のコンパイラを混在して使うことができたりして楽ができることになります。 独自 OS を作るってことは CPU メーカやコンパイラメーカの提案してきた ABI ではなく、独自のオレオレ ABI を採用することも可能です。オレオレ ABI を採用する以上は、それに適合するコンパイラやリンカーやデバッガを自作しなければならないですが。 なのであなたの・その本の著者が作ろうとしているオレオレ OS が「割り込みハンドラで SSE レジスタを使ってよい」という規則を採用するなら、今使っている [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") の規則と喧嘩しているので [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") のほうにパッチを当てて [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") 側の規則変更を行う必要があります。逆にオレオレ OS を [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") の仕様に合わせて規則選定するなら「割り込みハンドラで SSE レジスタを使っちゃならない」わけです。 全く同様のことが [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示") でも言えます。よって * [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示") / [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") のデフォルト ABI 規則をオレオレ OS で採用する なら [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示") / [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") に特別なオプションを指定しなくてよい * [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示") / [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") のデフォルト ABI 規則をオレオレ OS で採用しない なら [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示") / [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示") の動作をオレオレ OS の規則にあうように変更するオプションを指定する必要がある 手元にある [cygwin](/questions/tagged/cygwin "'cygwin' のタグが付いた質問を表示") x86_64 用の [gcc](/questions/tagged/gcc "'gcc' のタグが付いた質問を表示")-10.2.0 と [clang](/questions/tagged/clang "'clang' のタグが付いた質問を表示")-8.0.1 では `__attribute__((interrupt))` を使ったとき生成される機械語が異なります(=採用している ABI が異なる)。そのため、コンパイラを変更するとかコンパイルオプションを変更するとは即ち ABI を変更するということになります。逆にいえば、採用した ABI に合致するような機械語を生成するコンパイルオプションを探して指定する必要があるってことです。 x86 でなくて独自 CPU を採用している組み込み系では、コンパイラの ABI 仕様に合わせて OS を設計したりしますので、コンパイラの変更 (ABI が異なる) は OS の仕様の策定しなおしだったりします。 Q1. Q2 とも「オレオレ OS の仕様次第。オレオレ OS で採用することにした ABI に合致するコードを生成させるようにコンパイルオプションを指定するとよい」という回答になるでしょう。