14 ポイント 投稿者 GN⁺ 2025-05-06 | まだコメントはありません。 | WhatsAppで共有
  • 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に接続
  • _sbrkheapメモリを _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

まだコメントはありません。

まだコメントはありません。