Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page

GNURL78でconst領域/Mirror領域をちょっと安全に使えるようにlinker scriptのASSERT()で小技(TIPS)を考えてみた

こんにちは。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 area
Please change some of them from 'const' to 'const __far'
Error: No room left for const data - the start address exceeds the mirror area
Please 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 area
Please check the map file ← .textが.rodataよりも上側の場合
Error: No room left for const data - the end address exceeds the mirror area
Please 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 area
Please 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
    .data 0xF9F00: AT(_mdata)
    {
        . = ALIGN(2);
        _data = .;
        *(.data)
        *(.data.*)
        . = ALIGN(2);
        _edata = .;
    } > RAM
    PROVIDE(__romdatacopysize = SIZEOF(.data));

あと、添付したプロジェクトのソースでは、テスト用に非常に大きいサイズの関数を作る為に以下の記述を使ってみました。これは、アセンブラの繰り返しマクロ .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 Commands
    sourceware.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 LMA
    sourceware.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 constructors
    extern 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 ax
    skip_inilist:
        movw    ax, hl
        addw    ax, bc
        movw    hl, ax
        br  $next_inilist
    done_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%20GCC
    renesasrulz.com/search?q=RL78%20GNU
    renesasrulz.com/search?q=GNURL78
     

  • こんにちは。NoMaYです。#3連投の3つ目です。

    ちなみに、今回のリンカスクリプトをe2 studio v7.5.0ののリンカスクリプトエディタで開いてみたところ、一応、開くことは出来ました。(以下の画面コピーを参照) ですが、元々のリンカスクリプトでも同じことが起きてしまいますが、Graphical Editorで開こうとするとVALIDATION Errors and Warningsダイアログが表示されてしまいます。(以下の4つ目の画面コピーの下部を参照。) なお、原因までは調べませんでした。




Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page