ベアメタル上のC標準ライブラリ


24

私は主にLinuxを移植したデバイスで開発を行っているため、標準Cライブラリは、標準化された動作を持つシステムコールを実装することにより、多くの機能を提供します。

ただし、ベアメタルの場合、基盤となるOSはありません。acライブラリの実装方法に関連する標準はありますか?または、異なるBSPを提供する新しいボードに切り替えるときにライブラリ実装の特性を再学習する必要がありますか?


4
あなたの質問の間違ったサイト。
ott--

8
Stack Overflowに属しているため、この質問をトピック外として閉じることを投票しています。
uint128_t

1
一般的にあなたはなしで行います。それらをサポートするためにオペレーティングシステムなしでそのようなものが必要なのはなぜですか?memcpyなど。ファイルシステムは、必ずしもそうではありませんが、fopen、closeなどが実装されていても、たとえばramに対して簡単です。printf()は非常に非常に重いので、大量のコードが必要です。すべてのI / Oは交換するか、交換します。newlibは非常に極端ですが、それができない場合は役立ちますが、とにかくシステムをバックエンドに実装する必要があるので、追加のレイヤーが必要ですか?
old_timer

12
この質問はソフトウェアに関するものですが、一般的にSOによって拒否された組み込みプログラミングに非常に固有のものです。ここにはすでにいくつかの良い答えがあるので、移行は適切ではありません。
デイブツイード

1
newlibは以下の回答で言及されていますが、newlib-nanoが有用であることに気付くかもしれません-リソースが制限された組み込みシステムで使用するためのストリップバージョンです。Cortex M0 MCUのプロジェクトで使用します。多数のコンパイラ(Atollic TrueSTUDIOが1つ)は、newlibまたはnewlib-nanoを使用するオプションを提供します。
jjmilburn

回答:


20

はい、標準、単にC標準ライブラリがあります。ライブラリ関数は「完全な」OS、またはOSをまったく必要としません。また、「ベアメタル」コードに合わせた多くの実装があります。Newlibは おそらく最もよく知られています。

Newlibを例にとると、主にシステム内でファイルとメモリの割り当てを処理する方法など、コア機能の小さなサブセットを記述する必要があります。共通のターゲットプラットフォームを使用している場合、誰かがこの仕事をすでに行っている可能性があります。

Linux(おそらくOSXも、おそらくcygwin / msysでも)とtype man strlenを使用している場合CONFORMING TO、のようなセクションが必要です。これにより、実装が特定の標準に準拠していることがわかります。このようにして、使用しているものが標準機能であるか、特定のOSに依存しているかを判断できます。


1
OSに依存せずにstdlib実装する方法について興味stdioがあります。以下のようにfopen()fclose()fread()fwrite()putc()getc()?またmalloc()、OS と通信せずにどのように機能しますか?
ロバートブリストージョンソン

4
Newlibには、その下に「libgloss」と呼ばれるレイヤーがあります。このレイヤーには、プラットフォーム用の数十個の関数が含まれています(または作成します)。たとえば、ハードウェアのUARTを知っているgetcharand putchar; 次にprintf、これらの上にNewlibレイヤーがあります。ファイルI / Oも同様にいくつかのプリミティブに依存します。
ブライアンドラモンド

ええ、私はパイプの2番目の段落を注意深く読みませんでした。扱う以外stdinstdoutし、stderr (の世話をとるputchar()と、getchar()お使いのプラットフォームは、ファイルストレージを持っている場合は/からUARTへのI / Oを指示している)、フラッシュと同じように、あなたはまた、そのための書き込み糊を持っています。あなたがするための手段を持っている必要がありますmalloc()free()。これらの問題を処理すれば、埋め込みターゲットでポータブルCを実行できると思います(いいえargvもなしargc)。
ロバートブリストージョンソン

2
NEWLIBもある巨大な ...あなたはMCUの1または2kBのコード空間のを扱っている場合
ブライアン・ドラモンド

2
@dwelch独自のOSを作成するのではなく、Cライブラリを作成します。あなたがそれを望まないなら、ええ、それは不必要に大きいです。
パイプ

8

acライブラリの実装方法に関連する標準はありますか?または、異なるBSPを提供する新しいボードに切り替えるときにライブラリ実装の特性を再学習する必要がありますか?

まず、C標準では、「ホスト型」実装とは対照的に「自立型」実装と呼ばれるものを定義しています(これは、私たちの多くが知っている、基盤となるOSがサポートするC関数の全範囲です)。

「自立型」実装では、Cライブラリヘッダーのサブセット、つまりサポートを必要としないもの、または関数の定義(#definesとtypedefs のみを行う)さえ定義する必要があります。

  • <float.h>
  • <iso646.h>
  • <limits.h>
  • <stdalign.h>
  • <stdarg.h>
  • <stdbool.h>
  • <stddef.h>
  • <stdint.h>
  • <stdnoreturn.h>

ホストされた実装に向けて次の一歩を踏み出すと、「システム」とのインターフェースを実際に必要とする関数は非常に少なく、残りのライブラリはそれらの「プリミティブ」の上に実装可能であることがわかります。 「。PDCLibの実装では、libを新しいプラットフォームに移植するときに簡単に識別できるように、別のサブディレクトリにそれらを分離するための努力をしました(括弧内のLinuxポートの例):

  • getenv()extern char * * environ
  • system()fork()/ execve()/ wait()
  • malloc()およびfree()brk()/ sbrk()
  • _Exit()_exit()
  • time() (まだ実装されていません)

そして<stdio.h>(おそらく、最も「OSに関係する」C99ヘッダー):

  • ファイルを開く何らかの方法(open()
  • それを閉じる何らかの方法(close()
  • 削除する方法(unlink()
  • 名前を変更する何らかの方法(link()/ unlink()
  • 何らかの書き込み方法(write()
  • そこから読み取る何らかの方法(read()
  • 内部で再配置する何らかの方法(lseek()

ライブラリの特定の詳細はオプションであり、標準は単に標準的な方法で実装することを提供するだけでありそのような実装を要件にしません。

  • time()機能は、法的にだけ返すことが(time_t)-1何の計時メカニックが利用できない場合。

  • 記載されているシグナルハンドラは<signal.h>、への呼び出し以外で呼び出す必要はありません。raise()システムが実際にアプリケーションに何かを送信する必要はありませんSIGSEGV

  • <threads.h>(明らかな理由で)OS に非常に依存しているC11ヘッダーは、実装で定義されている場合、まったく提供する必要はありません__STDC_NO_THREADS__...

他にも例はありますが、今は手元にありません。

ライブラリの残りの部分は、環境の助けなしに実装できます。(*)


(*)警告:PDCLibの実装はまだ完全ではないので、1つまたは2つを見落としているかもしれません。;-)


4

標準Cは、実際にはオペレーティング環境とは別に定義されます。ホストOSが存在するという仮定は行われず、ホストに依存する部分はそのように定義されます。

つまり、C標準はすでにかなりベアメタルです。

もちろん、私たちが大好きな言語の部分であるライブラリは、多くの場合、コア言語が特定のホストをホストする場所です。したがって、多くのベアメタルプラットフォームツールに見られる典型的な「xxx-lib」クロスコンパイラーです。


3

Newlib最小実行可能サンプル

ここでは、QEMUでのnewlibの動作を示す高度に自動化され、文書化された例を提供します。

newlibを使用して、ベアメタルプラットフォーム用の独自のシステムコールを実装します。

たとえば、上記の例では、プログラム例がありますexit.c

#include <stdio.h>
#include <stdlib.h>

void main(void) {
    exit(0);
}

そして、別のCファイルにはcommon.c、我々は実装exitしてARMはセミホスティング

void _exit(int status) {
    __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
}

実装する他の典型的なシステムコールは次のとおりです。

  • write結果をホストに出力します。これは次のいずれかで実行できます。

    • よりセミホスティング
    • UARTハードウェア
  • brkのためにmalloc

    ページングを気にする必要がないため、ベアメタルで簡単です!

TODO ZephyrFreeRTOSなどの本格的なRTOSに移行せずに、プリコールスケジューリングのシステムコールの実行に到達するのが現実的かどうか疑問に思います。

Newlibのすばらしい点は、OS固有ではないものをすべてstring.h実装し、OSスタブだけを実装できることです。

また、すべてのスタブを実装する必要はなく、必要なスタブのみを実装する必要があります。たとえば、プログラムで必要なのがのみの場合exit、を提供する必要はありませんprint

NewlibソースツリーにはすでにARMセミホスティング実装を含むいくつかの実装newlib/libc/sys/armがありますが、ほとんどの場合、独自の実装が必要です。ただし、タスクの基盤はしっかりしています。

Newlibをセットアップする最も簡単な方法は、crosstool-NGを使用して独自のコンパイラを構築することです。NewlibをCライブラリとして使用することを伝えるだけです。私のセットアップでは、このスクリプトを使用して自動的に処理しますcrosstool_ng_config

C ++も動作すると思いますが、TODOでテストします。


3
@downvoters:情報を学び、改善できるように説明してください。うまくいけば、将来の読者がウェブで利用可能な唯一の入門Newlibセットアップの価値を見ることができることを願っています:-)
Ciro Santilli新疆改造中心法轮功六四事件

2

ベアメタルで使用する場合、未実装の依存関係をいくつか発見し、それらを処理する必要があります。これらの依存関係はすべて、システムの性格に応じて内部を調整することに関するものです。たとえば、内部でmalloc()を使用するsprintf()を使用しようとしたとき。Mallocには、コード内のフックとして「t_sbrk」関数シンボルがあります。これは、ハードウェアの制約を強制するためにユーザーが実装する必要があります。ここで、それを実装するか、sprintfだけでなく、主に他の用途のために組み込みハードウェアに対してより良いものを作成できると思う場合は、独自のmalloc()を作成します。


sprintfにmalloc()が必要なのはなぜですか?
supercat

知りません。あなたのポイントはすでに持っているバッファだと思いますか?しかし、printfでさえmallocを必要としません。要求された出力の計算がスタック割り当て(関数の動的変数)の予測よりも重い場合、いくつかの内部変数を動的に割り当てることができますか?sprintfにはmalloc(arm-none-eabi-newlib)が必要だったと確信しています。今、私はコンピューター(glibc)でsprintfを使用する簡単なプログラムを実験しました。mallocとは呼ばれませんでした。その後、printfを使用しました。mallocと呼ばれます。Mallocは偽物で、常に0を返していました。しかし、うまくいきました。彼らは文字列と10進変数を印刷することになっていました。@supercat
アイハン

私は自分のアプリケーションが使用する形式をサポートするためにカスタマイズしたprintfまたは同様のメソッドのいくつかのバージョンを自分で作成しました。10進出力には、可能な限り長い数を保持するのに十分な長さのバッファーが必要ですが、それ以外の場合、ベースルーチンは、その最初のメンバーが出力されるデータと共にその構造へのポインターを受け入れる出力関数である構造を受け入れます。このような設計により、cursesスタイルのコンソール、ソケットなどに出力するprintfバリアントを追加することができます。そのようなことで「malloc」は必要ありませんでした。
-supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.