私は Atmel SAM7S256 マイクロコントローラー用のソフトウェアをゼロから作成している大学のプロジェクトに取り組んでいます。今回はリンカー スクリプトとアセンブリ言語の知識が必要なため、以前に使用した他の MCU よりも詳細です。
SAM7/ARM プロジェクトをゼロから開始する方法を完全に理解するために、SAM7S チップのサンプル プロジェクトを精査してきました。注目すべき例は、Miro Samek の「Building Bare-Metal ARM Systems with GNU」チュートリアル(この質問のコードの出典) です。また、sourceware.org からリンカとアセンブラのドキュメントを読むのにも多くの時間を費やしました。
次のリンカ スクリプトの大部分を理解できて、とてもうれしく思います。ロケーション カウンターに関して、私には理解できないことが 1 つだけあります。以下は、上記のチュートリアルで提供されるリンカー スクリプトです。
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_vectors)
MEMORY { /* memory map of AT91SAM7S64 */
ROM (rx) : ORIGIN = 0x00100000, LENGTH = 64k
RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 16k
}
/* The sizes of the stacks used by the application. NOTE: you need to adjust */
C_STACK_SIZE = 512;
IRQ_STACK_SIZE = 0;
FIQ_STACK_SIZE = 0;
SVC_STACK_SIZE = 0;
ABT_STACK_SIZE = 0;
UND_STACK_SIZE = 0;
/* The size of the heap used by the application. NOTE: you need to adjust */
HEAP_SIZE = 0;
SECTIONS {
.reset : {
*startup.o (.text) /* startup code (ARM vectors and reset handler) */
. = ALIGN(0x4);
} >ROM
.ramvect : { /* used for vectors remapped to RAM */
__ram_start = .;
. = 0x40;
} >RAM
.fastcode : {
__fastcode_load = LOADADDR (.fastcode);
__fastcode_start = .;
*(.glue_7t) *(.glue_7)
*isr.o (.text.*)
*(.text.fastcode)
*(.text.Blinky_dispatch)
/* add other modules here ... */
. = ALIGN (4);
__fastcode_end = .;
} >RAM AT>ROM
.text : {
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb (NOTE: placed already in .fastcode) */
*(.glue_7t)/* glue thumb to arm (NOTE: placed already in .fastcode) */
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* global symbol at end of code */
} >ROM
.preinit_array : {
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >ROM
.init_array : {
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >ROM
.fini_array : {
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >ROM
.data : {
__data_load = LOADADDR (.data);
__data_start = .;
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .;
} >RAM AT>ROM
.bss : {
__bss_start__ = . ;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = .;
} >RAM
PROVIDE ( end = _ebss );
PROVIDE ( _end = _ebss );
PROVIDE ( __end__ = _ebss );
.heap : {
__heap_start__ = . ;
. = . + HEAP_SIZE;
. = ALIGN(4);
__heap_end__ = . ;
} >RAM
.stack : {
__stack_start__ = . ;
. += IRQ_STACK_SIZE;
. = ALIGN (4);
__irq_stack_top__ = . ;
. += FIQ_STACK_SIZE;
. = ALIGN (4);
__fiq_stack_top__ = . ;
. += SVC_STACK_SIZE;
. = ALIGN (4);
__svc_stack_top__ = . ;
. += ABT_STACK_SIZE;
. = ALIGN (4);
__abt_stack_top__ = . ;
. += UND_STACK_SIZE;
. = ALIGN (4);
__und_stack_top__ = . ;
. += C_STACK_SIZE;
. = ALIGN (4);
__c_stack_top__ = . ;
__stack_end__ = .;
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ : {
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}
例全体 (.ramvect、.fastcode、および .stack セクションなど) には、 などのシンボル定義があります__ram_start = .;
。これらのアドレスは、MCU の RAM 内の正しい位置を初期化するために、スタートアップ アセンブリ コードと初期化 C コードによって使用されます。
私が理解に問題を抱えているのは、これらのシンボル定義がどのようにして正しい値が割り当てられるかということです。これは起こります。スクリプトは正しいですが、方法がわかりません。
私が理解しているように、セクション内でロケーション カウンターを使用すると、セクション自体の仮想メモリ アドレス (VMA) からの相対オフセットのみが含まれます。
したがって、たとえば、行__ram_start = .;
では、__ram_start に 0x0 の値が割り当てられると予想されます。これは、.ramvect セクションの最初に位置カウンターの値が割り当てられているためです。ただし、初期化コードが正しく機能するためには (実際に機能します)、__ram_start を 0x00200000 (RAM の開始アドレス) として割り当てる必要があります。
行が代わりに__ram_start = ABSOLUTE(.);
または__ram_start = ADDR(.ramvect);
.
__fastcode_start
と についても同様__stack_start__
です。それらをすべてアドレス 0x0 として定義することはできません。そうしないと、プログラムが機能しません。しかし、ここにリンクされているドキュメントは、それが起こっていることを示唆しているようです。ドキュメントからの引用は次のとおりです。
ノート: 。実際には、現在含まれているオブジェクトの先頭からのバイト オフセットを参照します。通常、これは SECTIONS ステートメントであり、その開始アドレスは 0 であるため、. 絶対アドレスとして使用できます。もしも 。ただし、セクション記述内で使用される場合は、絶対アドレスではなく、そのセクションの先頭からのバイト オフセットを参照します。
したがって、これらのシンボル割り当て中のロケーション カウンター値は、対応するセクション VMA からのオフセットである必要があります。したがって、これらの「_start」シンボルはすべて0x0 に設定されているはずです。これはプログラムを壊します。
だから明らかに私は何かが欠けています。ロケーション カウンター値を (セクション内の) シンボルに割り当てると、デフォルトで ABSOLUTE() が使用されるという単純な結果になると思います。しかし、これを確認する明確な説明をどこにも見つけることができませんでした。
誰かがこれを解決できる場合は、事前に感謝します。