1.11.3 スタックポインタの初期化とヒープの上下限

C ライブラリを使用する場合は、スタックポインタの開始位置を指定する必要があります。ヒープを使用する ARM ライブラリ関数(malloc()calloc() など)を使用する場合や、main()argc および argv コマンドライン引数を定義する場合は、ヒープが最初に使用するメモリ領域も指定する必要があります。

スタックポインタの開始位置を必ず指定する必要があります。初期スタックポインタは、8 バイトの倍数に境界整列されている必要があります。
たとえば、以下のようなヒープを設定する必要があるとします。
  • ヒープを使用する ARM ライブラリ関数(malloc()calloc() など)を使用します。
  • main()argc および argv コマンドライン引数を定義します。
C ライブラリの初期化コードを使用する場合は、以下のいずれかの方法でスタックおよびヒープを設定します。
  • シンボル __initial_sp__heap_base、および __heap_limit を使用します。
  • スキャッタファイルを使用して、ARM_LIB_STACKHEAPARM_LIB_STACK、または ARM_LIB_HEAP 領域を定義します。
  • __user_setup_stackheap() または __user_initial_stackheap() を実装します。

microlib では、最初の 2 つの方法のみが、スタックポインタの開始位置とヒープの上下限を定義するための方法としてサポートされます。
C ライブラリの初期化コードを使用しない場合は(1.7.1 C ライブラリを使用しないアプリケーションの作成 を参照)、以下の方法でスタックおよびヒープを設定します。
  • アプリケーションのエントリポイントで、スタックポインタを手動で設定します。
  • _init_alloc() を呼び出して初期ヒープ領域を設定し、後でメモリを追加する必要がある場合は、__rt_heap_extend() を実装します。

シンボルで使用するスタックおよびヒープの設定

スタックの一番上を指すシンボル __initial_sp を定義します。
ヒープを使用している場合は、シンボル __heap_base および __heap_limit も定義します。
これらのシンボルは、アセンブリ言語ファイルで定義するか、または C で組み込みアセンブラを使用して定義できます。
以下に例を示します。
__asm void dummy_function(void)
{
    EXPORT __initial_sp
    EXPORT __heap_base
    EXPORT __heap_limit
__initial_sp EQU STACK_BASE
__heap_base EQU HEAP_BASE
__heap_limit EQU (HEAP_BASE + HEAP_SIZE)
}
定数 STACK_BASEHEAP_BASE、および HEAP_SIZE は、以下に示すように、stack.h などのヘッダファイル内に定義できます。
/* stack.h */
#define HEAP_BASE 0x20100000  /* メモリアドレスの例 */
#define STACK_BASE 0x20200000
#define HEAP_SIZE ((STACK_BASE-HEAP_BASE)/2)
#define STACK_SIZE ((STACK_BASE-HEAP_BASE)/2)

初期スタックポインタとヒープの上下限を指定するためのこの方法は、標準 C ライブラリ(standardlib)と マイクロ C ライブラリ(microlib)の両方でサポートされます。

スキャッタファイルを使用したスタックとヒープの設定

スキャッタファイル内で、以下のいずれかの操作を行います。
  • ARM_LIB_STACK 領域と ARM_LIB_HEAP 領域を定義します。
    ヒープを使用しない場合は、ARM_LIB_STACK 領域のみを定義します。
  • ARM_LIB_STACKHEAP 領域を定義します。
    ARM_LIB_STACKHEAP 領域を定義した場合、スタックは領域の先頭から始まります。ヒープは末尾から始まります。

__user_setup_stackheap() または __user_initial_stackheap() を使用したスタックとヒープの設定

__user_setup_stackheap() を実装してスタックポインタを設定し、初期ヒープ領域の上下限を返します。
__user_initial_stackheap() を使用している従来のコードがあり、__user_initial_stackheap()__user_setup_stackheap() で置き換えたくない場合は、__user_initial_stackheap() を引き続き使用します。

__user_initial_stackheap() の実装が以下に該当する場合を除き、ARM では、__user_initial_stackheap()__user_setup_stackheap() に置き換えることをお勧めします。
  • 適切なスタックを作成する前に独自のテンポラリスタックが必要になるなど、十分複雑に特殊化されている。
  • ユーザに固有の特殊な要件があり、アセンブリ言語ではなく C で実装する必要がある。

_init_alloc() と __rt_heap_extend() を使用したベアマシン C からのヒープの設定

ベアマシン C( main() を定義しておらず、C ライブラリを初期化しないアプリケーション)からヒープの実装を使用する場合、ヒープの上限と下限を定義し、ヒープ拡張機能を提供する必要があります。
  1. _init_alloc(base, top) を呼び出して、ヒープとして管理するメモリの上限と下限を定義します。

    _init_alloc(base, top) のパラメータは、8 バイトで整列されている必要があります。
  2. 関数 unsigned __rt_heap_extend( unsigned size, void **block) を定義して、ヒープが一杯になったときにヒープを拡張するための呼び出しを処理します。

スタックおよびヒープの衝突検出

デフォルトでは、ヒープ用のメモリがスタックに近い領域に配置されたメモリとオーバーラップする可能性がある場合、ヒープとスタックの潜在的な衝突が自動的に検出され、要求されたヒープ割り当ては失敗します。この自動衝突検出が必要でない場合は、#pragma import__use_two_region_memory を使用してこの機能を無効にすることにより、わずかですがコードサイズを小さくできます。

メモリ割り当て関数(malloc()realloc()calloc()、および posix_memalign())は、現在のスタックポインタと衝突する割り当ての検出を試みます。ただし、その試みが常に成功するとは限りません。
スタックへのヒープの拡張を自動的に検出することは可能ですが、ヒープメモリへのスタックの拡張を自動的に検出することはできません。
従来の目的のためには、これらの方法および動作をすべてバイパスすることができます。そのためには、以下の関数を定義して、独自にスタックおよびヒープメモリの管理を行います。
  • __rt_stackheap_init()
  • __rt_heap_extend()

実行時のヒープサイズの拡張

プログラムの起動時に指定されたメモリ領域以外のメモリ領域にヒープを拡張できるようにするには、関数 __user_heap_extend() を定義し直します。
__user_heap_extend() は、ヒープのサイズ拡張で使用するメモリブロックを返します。
関連する概念
1.11.4 __user_initial_stackheap() の従来のサポート
関連する参考文書
4.50 __user_heap_extend()
4.51 __user_heap_extent()
4.25 __rt_heap_extend()
4.29 __rt_stackheap_init()
4.52 __user_setup_stackheap()
4.53 __vectab_stack_and_reset
関連情報
スキャッタファイルを使用したスタックとヒープの指定
非機密扱いPDF file icon PDF 版ARM DUI0808CJ
Copyright © 2014, 2015 ARM.All rights reserved.