こんにちは。NoMaYです。#3連投の1つ目です。先日、以下のスレッドにて、GNURL78のconst領域/Mirror領域のリンカでの取り扱いがあまり安心出来るようなものでは無かったことを知りました。そこで、この領域を幾らかでも安心して使えるようにならないものかと試行錯誤してみたところ、リンカスクリプトでASSERT()を利用してconst領域とMirror領域の関係が適切かどうかチェック出来ることが分かりました。そこで、RL78/G14のプロジェクトを作成し、ファイル一式を以下に固めました。(e2 studio v7.5.0+GNURL78 2019q2(4.9.2.201902)でビルド)(zipファイルをe2 studioに直接インポート可能)gnurl78_linker_script_improvement_20190823.zip 193KB含まれるプロジェクト・large_const_small_text (.textセクションが.rodataセクションの下側(0番地側))・large_const_large_text (.textセクションが.rodataセクションの上側(0xFFFFF番地側))(以前の投稿のRL78 FreeRTOSのプロジェクト 1, 2 から派生させたのでちょっと独特なものですが、リンカスクリプトに関しては今回の変更箇所以外はe2 studioで生成した標準的なものです。)「e2studio + GCC for Renesas RL78」ROMサイズが少し大きくなるといろいろ出る不具合【 near領域のROMサイズに注意しましょう】japan.renesasrulz.com/cafe_rene/f/forum21/5899/e2studio-gcc-for-renesas-rl78-rom-near-rom今回、const領域とMirror領域の関係が適切かどうかチェックして、問題があれば以下のエラーメッセージを表示させるようにしてみました。
Error: Too much const data - the size exceeds the mirror areaPlease change some of them from 'const' to 'const __far'
Error: No room left for const data - the start address exceeds the mirror areaPlease move the .text section from lower address to higher address ← .textが.rodataよりも下側の場合Error: No room left for const data - the start address exceeds the mirror areaPlease check the map file ← .textが.rodataよりも上側の場合
Error: No room left for const data - the end address exceeds the mirror areaPlease move the .text section from lower address to higher address ← .textが.rodataよりも下側の場合Error: No room left for const data - the end address exceeds the mirror areaPlease check the map file ← .textが.rodataよりも上側の場合
e2 studioの画面コピー(const領域のサイズがMirror領域のサイズを超えた場合の例)今回、エラーをチェックする為のリンカスクリプトの変更として、以下の記述を追加しました。(以下の赤文字箇所)
PROVIDE(__mirror = 0x3000); PROVIDE(__mirror_end = __mirror + 28416); PROVIDE(__rodata = ADDR(.rodata)); .rodata MAX(., __mirror): { . = ALIGN(2); *(.rodata) *(.rodata.*) _erodata = .; ASSERT(!(SIZEOF(.rodata) > (__mirror_end - __mirror)), "Error: Too much const data - the size exceeds the mirror area"); ASSERT(!(SIZEOF(.rodata) > (__mirror_end - __mirror)), "Please change some of them from 'const' to 'const __far'"); ASSERT(!((SIZEOF(.rodata) <= (__mirror_end - __mirror)) && (SIZEOF(.rodata) > 0) && (ABSOLUTE(__rodata) >= __mirror_end)), "Error: No room left for const data - the start address exceeds the mirror area"); ASSERT(!((SIZEOF(.rodata) <= (__mirror_end - __mirror)) && (SIZEOF(.rodata) > 0) && (ABSOLUTE(__rodata) < __mirror_end) && (ABSOLUTE(.) > __mirror_end)), "Error: No room left for const data - the end address exceeds the mirror area"); ASSERT(!((SIZEOF(.rodata) <= (__mirror_end - __mirror)) && (SIZEOF(.rodata) > 0) && (ABSOLUTE(.) > __mirror_end) && (ADDR(.text) < __rodata)), "Please move the .text section from lower address to higher address"); ASSERT(!((SIZEOF(.rodata) <= (__mirror_end - __mirror)) && (SIZEOF(.rodata) > 0) && (ABSOLUTE(.) > __mirror_end) && (__rodata < ADDR(.text))), "Please check the map file"); } > ROM
なお、__mirrorと__mirror_endへの代入に使われている値はリンカスクリプトの以下の箇所に記述されている値と同じものです。(リンカのマニュアルを見てはみたのですが、以下の箇所の値を直接利用する方法は見つかりませんでした。) よって、やろうと思えば、e2 studioで生成されるGNURL78プロジェクトのテンプレートのソースがあるフォルダのリンカスクリプトを全部一度に変更するバッチファイル/スクリプトファイルを作成することも可能、な筈です、、、
MEMORY{ VEC : ORIGIN = 0x0, LENGTH = 4 IVEC : ORIGIN = 0x4, LENGTH = 188 OPT : ORIGIN = 0xC0, LENGTH = 4 SEC_ID : ORIGIN = 0xC4, LENGTH = 10 OCDSTAD : ORIGIN = 0xCE, LENGTH = 10 OCDROM : ORIGIN = 0x3FE00, LENGTH = 512 ROM : ORIGIN = 0xD8, LENGTH = 261416 MIRROR : ORIGIN = 0xF3000, LENGTH = 28416 RAM : ORIGIN = 0xF9F00, LENGTH = 24576}
また、const領域とMirror領域の関係が不適切になってしまう原因の1つには.textセクションが.rodataセクションの下側(0番地側)に収まらなくなってしまうことがあるのですが、その場合に.textセクションを.rodataセクションの上側(0xFFFFF番地側)へ移し易くする為、以下の.mdataセクションをリンカスクリプトに追加しました。(以下の赤文字箇所)(他に橙文字箇所を削除)従来
.tors : { __CTOR_LIST__ = .; . = ALIGN(2); ___ctors = .; *(.ctors) ___ctors_end = .; __CTOR_END__ = .; __DTOR_LIST__ = .; ___dtors = .; *(.dtors) ___dtors_end = .; __DTOR_END__ = .; . = ALIGN(2); _mdata = .; } > ROM .text (. + __romdatacopysize): { *(.text) *(.text.*) etext = .; . = ALIGN(2); } > ROM
.data 0xF9F00: AT(_mdata) { . = ALIGN(2); _data = .; *(.data) *(.data.*) . = ALIGN(2); _edata = .; } > RAM PROVIDE(__romdatacopysize = SIZEOF(.data));
今回
.tors : /* TODO: Does this section work? Should below items be in the .rodata section? */ { __CTOR_LIST__ = .; . = ALIGN(2); ___ctors = .; *(.ctors) ___ctors_end = .; __CTOR_END__ = .; __DTOR_LIST__ = .; ___dtors = .; *(.dtors) ___dtors_end = .; __DTOR_END__ = .; . = ALIGN(2); } > ROM .mdata (NOLOAD) : { _mdata = .; . += __romdatacopysize; } > ROM .text : { *(.text) *(.text.*) etext = .; . = ALIGN(2); } > ROM
あと、添付したプロジェクトのソースでは、テスト用に非常に大きいサイズの関数を作る為に以下の記述を使ってみました。これは、アセンブラの繰り返しマクロ .rept ~ .endr で任意の数のnopを生成させるものです。
__attribute__ ((noinline)) void large_func(void);
void large_func(void){ //__asm volatile (".rept 0xA000"); /* the const startd address exceeds the mirror area */ //__asm volatile (".rept 0x2000"); /* the const end address exceeds the mirror area */ __asm volatile (".rept 0"); __asm volatile ("nop"); __asm volatile (".endr");}
こんにちは。NoMaYです。#3連投の2つ目です。今回、リンカスクリプトでエラーをチェックする方法としてASSERT()を使ってみたのですが、これはe2 studioが生成したリンカスクリプトで既に以下のように使われていたことに気付いたことから使ってみました。
PROVIDE(stack_size = 0x64); .stack 0xFFEDC (NOLOAD) : AT(0xFFEDC) { _stack = .; ASSERT((_stack > (_end + stack_size)), "Error: Too much data - no room left for the stack"); } > RAM
なお、リンカスクリプトのASSERT()の説明は以下のウェブページにあります。3.4.5 Other Linker Script Commandssourceware.org/binutils/docs/ld/Miscellaneous-Commands.html#Miscellaneous-Commands
ASSERT(exp, message) Ensure that exp is non-zero. If it is zero, then exit the linker with an error code, and print message.
PROVIDE (__stack_size = 0x100); .stack : { PROVIDE (__stack = .); ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack"); }
また、.mdataセクションというセクションですが、以下のウェブページに.mdataセクションの使用例があったことから使ってみました。(ただし、GNURL78では以下の.mdataセクションは.dataセクションに相当します。)3.6.8.2 Output Section LMAsourceware.org/binutils/docs/ld/Output-Section-LMA.html#Output-Section-LMA
SECTIONS { .text 0x1000 : { *(.text) _etext = . ; } .mdata 0x2000 : AT ( ADDR (.text) + SIZEOF (.text) ) { _data = . ; *(.data); _edata = . ; } .bss 0x3000 : { _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}}
他方、実は、C++に関係する以下のセクションに関しては何か腑に落ちないものを感じていて、後で調べてみようかと思い、TODOで目印代わりのコメントを入れました。
.tors : /* TODO: Does this section work? Should below items be in the .rodata section? */ { __CTOR_LIST__ = .; . = ALIGN(2); ___ctors = .; *(.ctors) ___ctors_end = .; __CTOR_END__ = .; __DTOR_LIST__ = .; ___dtors = .; *(.dtors) ___dtors_end = .; __DTOR_END__ = .; . = ALIGN(2); } > ROM
PROVIDE(__rl78_abs__ = 0); /* TODO: What is this? */ .init : /* TODO: Is there any restriction to locate this section? */ { *(.init) } > ROM .fini : /* TODO: Is there any restriction to locate this section? */ { KEEP(*(.fini)) } > ROM .got : /* TODO: Is there any restriction to locate this section? */ { *(.got) *(.got.plt) } > ROM
腑に落ちない点の1つ目は.torsセクションですが、上記のリンカスクリプトではfar領域に存在するのに、e2 studioで生成したC++ソースの以下の初期化処理ではnear領域に存在することを想定していて、両者がミスマッチな気がするからです。
//Initialize global constructorsextern void __main(){ static int initialized; if (! initialized) { typedef void (*pfunc) (); extern pfunc __ctors[]; extern pfunc __ctors_end[]; pfunc *p; initialized = 1; for (p = __ctors_end; p > __ctors; ) (*--p) (); }}
腑に落ちない点の2つ目は.init/.fini/.gotの各セクションですが、e2 studioで生成したC++プロジェクトのスタートアップルーチンに、これらのセクションに配置されたものを取り扱うコードが無いことです。(なので、これらの配置に関してエラーチェックをした方が良いのか/するのであればどの様なエラーチェックをした方が良いのか、判断しかねている状況です。) 腑に落ちない点の3つ目は__rl78_abs__は何の為にあるのか分からないことです。e2 studioで生成されるGNURL78プロジェクトのテンプレートのソースがあるフォルダでソースをgrepしてみたのですが、使われているソースがありませんでした。ちなみに、がじぇるねのGR-COTTONのRL78 Arduinoライブラリ(実装にC++を使用)のe2 studioプロジェクト(cotton_sketch_v203_e2v7.zip)のリンカスクリプトやスタートアップルーチンは、以下の通りになっていました。(なお、コンパイラはGNURL78 4.9.2.201701となっています。)cotton_sketch_v203_e2v7/cotton_sketch/rl78_R5F100GJAFB.ld
.data 0xfaf90 : { /* used for FDL(EEPROM) from 0xfaf00 to 0xfaf87*/ . = ALIGN(2); PROVIDE (__datastart = .); KEEP (*(.jcr)) *(.data.rel.ro.local) *(.data.rel.ro*) *(.dynamic) *(.data D .data.* .gnu.linkonce.d.*) ; KEEP(*reset_program.o(*.data)) KEEP (*(.gnu.linkonce.d.*personality*)) SORT(CONSTRUCTORS) *(.data1) *(.got.plt) *(.got) /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ . = ALIGN(2); *(.sdata .sdata.* .gnu.linkonce.s.* D_2 D_1) . = ALIGN(2); _edata = .; PROVIDE (edata = .); PROVIDE (__dataend = .); } > RAM AT> ROM
.rodata (MAX(__romdatastart + __romdatacopysize, 0x3000)) : { . = ALIGN(2); *(.plt) *(.rodata C C_2 C_1 .rodata.* .gnu.linkonce.r.*) *(.rodata1) *(.eh_frame_hdr) KEEP (*(.eh_frame)) KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) PROVIDE (__preinit_array_start = .); KEEP (*(.preinit_array)) PROVIDE (__preinit_array_end = .); PROVIDE (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) PROVIDE (__init_array_end = .); PROVIDE (__fini_array_start = .); KEEP (*(.fini_array)) KEEP (*(SORT(.fini_array.*))) PROVIDE (__fini_array_end = .); LONG(0); /* Sentinel. */ /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin*.o(.ctors)) /* We don't want to include the .ctor section from from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) KEEP (*crtbegin*.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) } > ROM
.text : { PROVIDE (_start = .); *(.text P .stub .text.* .gnu.linkonce.t.*) *(PFDL_COD) ; KEEP(*reset_program.o(.text)) KEEP (*(.text.*personality*)) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) *(.interp .hash .dynsym .dynstr .gnu.version*) PROVIDE (__etext = .); PROVIDE (_etext = .); PROVIDE (etext = .); . = ALIGN(2); KEEP (*(.init)) KEEP (*(.fini)) } > ROM
cotton_sketch_v203_e2v7/cotton_sketch/arduino/rl78/reset_program.S
/* call the hardware initialiser */ call !!_init nop call !!__rl78_init
.global _rl78_run_preinit_array .type _rl78_run_preinit_array,@function_rl78_run_preinit_array: movw hl, #__preinit_array_start movw de, #__preinit_array_end movw bc, #-2 br $_rl78_run_inilist .global _rl78_run_init_array .type _rl78_run_init_array,@function_rl78_run_init_array: movw hl, #__init_array_start movw de, #__init_array_end movw bc, #2 br $_rl78_run_inilist .global _rl78_run_fini_array .type _rl78_run_fini_array,@function_rl78_run_fini_array: movw hl, #__fini_array_start movw de, #__fini_array_end movw bc, #-2 /* fall through */ ;; HL = start of list ;; DE = end of list ;; BC = step direction (+2 or -2)_rl78_run_inilist:next_inilist: movw ax, hl cmpw ax, de bz $done_inilist movw ax, [hl] cmpw ax, #-1 bz $skip_inilist cmpw ax, #0 bz $skip_inilist push ax push bc push de push hl call ax pop hl pop de pop bc pop axskip_inilist: movw ax, hl addw ax, bc movw hl, ax br $next_inilistdone_inilist: ret .section .init,"ax" .global __rl78_init__rl78_init: .section .fini,"ax" .global __rl78_fini__rl78_fini: call !!_rl78_run_fini_array
[メモ]gcc-renesas.com/forum/category/renesas-rl78-gcc/renesasrulz.com/search?q=RL78%20GCCrenesasrulz.com/search?q=RL78%20GNUrenesasrulz.com/search?q=GNURL78