OSなしで`printf`を実装する - Bare Metal環境でC標準ライブラリを活用
(popovicu.com)- Newlibライブラリを活用し、OSがなくても**
printfを含むC標準関数**を使えるようにする実装方法を紹介 - RISC-VアーキテクチャベースのBare Metal環境でUARTドライバおよびメモリ割り当て関数を自前で実装し、Newlibに接続
_write,_sbrk,_closeなど最小限のシステムコール関数の実装だけで、printfのような高度な機能も利用可能- Newlibベースのツールチェーンを作るために、RISC-V GCCツールチェーンとあわせた自動ビルドおよびリンカスクリプトの書き方を案内
- 結果として、UART出力、
scanf入力、動的メモリ割り当てまで動作するprintf環境の構築に成功
Software abstractions and C standard library
- 一般的なOSでは
printfを呼び出すと、カーネルのシステムコール、ターミナル層、フォントレンダリングなど多様な抽象化レイヤーが動作する - Bare Metal環境では、OSなしで直接入出力を制御する必要があり、そのためにドライバの自前実装が求められる
- Newlibは、完全なC標準ライブラリの代わりに最小機能だけを実装して拡張可能な構成を提供する
Newlib concept
printfは内部的に_writeのような単純な primitive 関数を基盤として実装されている- Newlibでは、初期状態ではすべての関数がダミー形式で定義されており、必要な部分だけを実装すれば残りは既定値を使える
- 開発者が必要な関数だけを実装すれば、柔軟にCライブラリ機能を利用可能
Cross-compilation toolchain
- x86_64/Linux → RISC-V のクロスコンパイルのため、GCCソースから直接ビルドが必要
- Newlibを標準Cライブラリとして設定したツールチェーンを構築し、RISC-V向けバイナリをビルドできるように設定
Toolchain details
- ツールチェーンのビルド時に
--prefix,--enable-multilib,--disable-gdb,--with-cmodel=medanyオプションを使用 medanyはRISC-Vで高位アドレスのメモリ領域へアクセス可能にする設定- ビルド完了後は
/opt/riscv-newlibパスで cross-compiler および Newlibライブラリを利用可能
Implementing the memory and UART building blocks
- QEMU環境の16550A UARTハードウェアアドレスへ直接アクセスして文字の送受信を実装
_write,_sbrk,_closeなどのシステムコール代替関数を実装してNewlibに接続_sbrkはheapメモリを_end地点から_stack_bottomまで拡張する方式で動作
Application example: input and output
main関数でprintf,scanfの利用が可能で、入力値も正常に処理される- echoはサポートしないが、
scanfを通じて文字列入力を受け取り出力できる - 別途ランタイムを実装し、スタック初期化とBSSセクションの zero-fill を行った後に
mainを呼び出す
Linker script
- 実行開始アドレスは
0x80000000で、その位置にランタイムコードを配置 .text,.rodata,.data,.bssの順でメモリを配置し、heapは_endから stack 直前までに設定- stackは64KB固定サイズで、最上位アドレスは
0x80000000 + 64MB ASSERT構文でheapとstackの衝突を防止
The ‘gotcha’ moment
- ツールチェーン設定時に
--with-cmodel=medanyを使う必要があり、0x80000000以上のアドレスを扱える機械語命令を生成可能になる - Cライブラリとアプリケーションコードでアドレスモデルが異なるとリンクエラーが発生する
Running the app
- MakefileによりクロスコンパイルとQEMU実行を自動化可能
-specs=nosys.specs,-nostartfiles,-T link.ldオプションでNewlibの最小設定とユーザー定義ランタイムを使用make debugを実行すると、QEMUコンソール上でUARTによる入力と出力が正常に動作qemu_debug.logにより実際の命令トレースを確認できる
Conclusion
- OSなしでも
printf,scanf,mallocなどを利用可能な構成をNewlibで実装 - Newlibのビルディングブロックベースの構造を活用し、必要な機能だけを最小限で実装することが中核戦略
- 今後はファイルシステム、メモリ管理などの追加機能実装も可能で、ライブラリ互換性を保ちながらBare Metalでも再利用可能
- プロジェクト全体の成果物は約220KBで、比較的小さく効率的な規模
GitHubソース: popovicu/bare-metal-cstdlib
まだコメントはありません。