18

コンテキスト:
私はARMターゲット、より具体的にはSTのCortex-M4Fマイクロコントローラーに取り組んでいます。このようなプラットフォーム(一般的にはマイクロコントローラー)で作業する場合、明らかにOSはありません。動作するC/C ++「環境」を取得するには(さらに、変数の初期化に関して標準に準拠するため)、明示的に呼び出す前に必要な最小限のセットアップを実行する、リセット時に実行される何らかのスタートアップコードが必要ですmain。私が示唆したように、このようなスタートアップコードは、初期化されたグローバル変数と静的変数(グローバルスコープなど)を初期化int foo = 42;し、他のグローバル変数(グローバルスコープなど)をゼロにする必要がありますint bar;。次に、必要に応じて、グローバルな「ctors」が呼び出されます。

マイクロコントローラーでは、これは単に、スタートアップコードが初期化されたグローバル(すべてセクション「.data」)ごとにフラッシュからRAMにデータをコピーし、他のグローバル(すべて「.bss」)をクリアする必要があることを意味します。私はGCCを使用しているので、そのようなスタートアップコードを提供する必要があり、インターネットで見つけた多数の例にバンドルされているいくつかのスタートアップコード(および関連するリンカースクリプト!)を、開発中の同じデモボードを使用して喜んで分析しました。 。

質問:
述べたように、私は多くのスタートアップコードを見てきました、そしてそれらは異なる方法でグローバルを初期化します、いくつかは他のものより空間と時間の点でより効率的です。しかし、それらにはすべて奇妙な共通点があります。それらは使用memsetmemcpy、代わりに手書きのループを使用して作業を行いました。可能な場合は標準関数(単純な「DRY原理」)を使用するのが自然に思えるので、最初の手書きループの代わりに次のことを試しました。

/* Initialize .data section */
ldr r0, DATA_LOAD
ldr r1, DATA_START
ldr r2, DATA_SIZE
bl  memcpy       /* memcpy(DATA_LOAD, DATA_START, DATA_SIZE); */

/* Initialize .bss section */
ldr r0, BSS_START
mov r1, #0
ldr r2, BSS_SIZE
bl  memset       /* memset(BSS_START, 0, BSS_SIZE); */

...そしてそれは完璧に機能しました。スペースの節約はごくわずかですが、今では明らかに単純です。

それで、私はそれについて考えました、そして私はこの場合手書きのループをする理由がわかりません:

  • memcpyとにかく実行可能ファイルにリンクされている可能性memsetが非常に高いです。プログラマーはそれを直接、または別のライブラリを介して間接的に使用するからです。
  • 小さいです。
  • 速度はスタートアップコードにとってそれほど重要な要素ではありませんが、それでも速度は速いと思われます。
  • それを間違えることはほぼ不可能です。

なぜスタートアップコードに頼らないのか、何か考えはありmemcpyますか?memset

4

3 に答える 3

21

memcpyスタートアップコードは、libcでの実装などについて想定したくないのではないかと思います。たとえば、の実装でmemcpyは、libc初期化コードによって設定されたグローバル変数を使用して、使用可能なcpu拡張機能を報告し、そのような操作をサポートするマシンで最適化されたSIMDコピーを提供します。初期の「crt」スタートアップコードが実行されている時点で、そのようなグローバルのストレージは完全に初期化されていない(ランダムなジャンクを含む)可能性があります。その場合、を呼び出すのは危険memcpyです。たとえあなたのために電話をかけることがうまくいったとしても、それはそれを機能させる実装の結果です(あるいはUBの予測できない結果でさえ...)。これはおそらく、crtコードが依存したいものではありません。

于 2013-03-17T03:14:11.003 に答える
15

標準ライブラリがリンクされているかどうかは、アプリケーション開発者の判断ですが(--nostdlibたとえば、使用される場合があります)、起動コードが必要であるため、想定することはできません。

さらに、スタートアップコードの目的は、Cコードを実行できる環境を確立することです。それが完了する前に、完全なランタイム環境を合理的に想定する可能性のあるライブラリコードが正しく実行されることは決してありません。問題の関数の場合、これは多くの場合問題ではない可能性がありますが、それを知ることはできません。

スタートアップコードは、少なくともスタックを確立し、静的データを初期化する必要があります。C++では、さらにグローバル静的オブジェクトのコンストラクターを呼び出します。標準ライブラリは、それらが確立されていると合理的に想定している可能性があるため、それ以前に標準ライブラリを使用すると、誤った動作が発生する可能性があります。

最後に、C言語とC標準ライブラリが別個のエンティティであることを明確にする必要があります。言語は必然的に独立できる必要があります。

于 2013-03-17T08:38:00.187 に答える
1

これは「memcy/memsetの内部状態に関する仮定」とは関係がないと思います。グローバルリソースを使用する可能性は低いです(ただし、使用する場合は奇妙なケースがいくつかあると思います)。

マイクロコントローラー上のすべてのスタートアップコードは、通常、この方法で「インラインアセンブラ」と記述されます。これは、コードの初期段階で実行され、スタックがまだ存在せず、MMUセットアップがまだ実行されていない可能性があるためです。したがって、初期化コードは、そのように単純なものをスタックに置くリスクを冒したくありません。関数呼び出しは物事をスタックに置きます。

したがって、これはたまたま静的ストレージのコピーダウンの初期化コードでしたが、他のそのような初期化コードでも同じインラインアセンブラを見つける可能性があります。たとえば、コピーダウンの前のどこかにアセンブラで記述された基本的なレジスタセットアップコードがあり、その周辺のどこかにアセンブラのMMUセットアップもあります。

于 2013-03-18T07:57:07.527 に答える