5

ARM Cortex-M0+ マイクロコントローラーで実行するためのソフトウェアを構築しています。関数の呼び出し時にセカンダリ プログラムとして実行される種類の USB ブートローダーが含まれています。memcpyコンパイル中の関数の挿入に問題があります。

バックグラウンド

リンカー スクリプトがすべての始まりです。そのほとんどは非常に簡単で標準的なものです。プログラムはそこに保存され.text、そこからも実行されます。すべてが.textチップのフラッシュセクションに保存されます。

奇妙なのは、ブートローダーが実行される部分です。ブートローダ コードを上書きせずにすべてのフラッシュを書き込めるようにするために、私のブートローダ エントリ ポイントは、マイクロコントローラの SRAM 部分へのブートローダ プログラムのコピーを開始し、そこから実行します。このようにして、ブートローダーは誤って自身を削除することなく、デバイス上のすべてのフラッシュを安全に消去できます。

これは、リンカー スクリプトで偽の「オーバーレイ」を実行することによって実装されます (実際OVERLAYの使用例は、私の使用例とはまったく一致しませんでした)。

/**
 * The bootloader and general ram live in the same area of memory
 * NOTE: The bootloader gets its own special RAM space and it lives on top
 * of both .data and .bss.
 */

_shared_start = .;
.bootloader _shared_start : AT(_end_flash)
{
    /* We keep the bootloader and its data together */
    _start_bootloader_flash = LOADADDR(.bootloader);
    _start_bootloader = .;
    *(.bootloader.data)
    *(.bootloader.data.*)
    . = ALIGN(1024); /* Interrupt vector tables must be aligned to a 1024-byte boundary */
    *(.bootloader.interrupt_vector_table)
    *(.bootloader)
    _end_bootloader = .;
}

.data _shared_start : AT(_end_flash + SIZEOF(.bootloader))
{
    _start_data_flash = LOADADDR(.data);
    _start_data = .;
    *(.data)
    *(.data.*)
    *(.shdata)
    _end_data = .;
}
. = _shared_start + SIZEOF (.data);
_bootloader_size = _end_bootloader - _start_bootloader;
_data_size = _end_data - _start_data;

_end_flashすべてのデータをフラッシュに保存した前のセクションの最後への参照です ( .text.rodata.init...基本的に読み取り専用のものはすべてそこにスタックします)。

これにより、セクション.data.bssセクションは通常 RAM に存在します。ただし、.bootloaderセクションは RAM の同じ場所にも存在します。両方のセクションは、コンパイル時に順番にフラッシュに保存されます。私のcrt0ルーチンでは、.dataセクションはフラッシュから RAM 内の適切なアドレス ( で指定_start_data) にコピーされ、.bssセクションはゼロになります。.textフラッシュから RAM にデータをコピーしてブートローダーを開始するセクションに追加のセクションを保存し、.dataとにあったものを上書きします.bss。ブートローダからの唯一の終了はシステム リセットであるため、実行中のプログラムのデータが破壊されても問題ありません。ブートローダを RAM にコピーした後、それを実行します。

質問

明らかに、オーバーレイされたプログラムをコンパイルし、すべての参照が揃っていることを確認する際に、いくつかの問題が発生する可能性があります。.data通常のプログラムからブートローダー コードにアクセスしたり、通常のプログラムやブートローダーからアクセスしたりする際に発生する問題を軽減するため.bssに、リンカー スクリプトに次の 3 行を追加しました。

NOCROSSREFS(.bootloader .text);
NOCROSSREFS(.bootloader .data);
NOCROSSREFS(.bootloader .bss);

.textここで、(ブートローダーによって消去される可能性がある)、.data(ブートローダーが上にある)、または.bss(ブートローダーがその上にある) セクションとセクションの間にクロスがあるときはいつでも.bootloader、コンパイラ エラーが発生します。発行済み。

実際にコードを書き始めるまで、これはうまくいきました。私のコードの一部には、いくつかの構造体のコピーなどが含まれています。どうやら、コンパイラはこれを行うことを決定しました(bootloader_関数は.bootloaderセクションに存在します):

20000340 <bootloader_usb_endp0_handler>:
...
20000398:   1c11        adds    r1, r2, #0
2000039a:   1c1a        adds    r2, r3, #0
2000039c:   f000 f8e0   bl  20000560 <__memcpy_veneer>
...
20000560 <__memcpy_veneer>:
20000560:   b401        push    {r0}
20000562:   4802        ldr r0, [pc, #8]    ; (2000056c <__memcpy_veneer+0xc>)
20000564:   4684        mov ip, r0
20000566:   bc01        pop {r0}
20000568:   4760        bx  ip
2000056a:   bf00        nop
2000056c:   00000869    andeq   r0, r0, r9, ror #16

私のチップのアーキテクチャでは、アドレス0x20000000まで0xE000000は SRAM に配置されています (実際にはデバイス上に 4Kb しかありません)。以下のアドレスはすべて0x1fffffc00フラッシュ セクションにあります。

.bootloader問題はこれです: 私のセクション ( bootloader_usb_endp0_handler) にある私の関数では、memcpy( 2000039c20000562、および2000056c) への参照が挿入されました。これは、特に構造体のコピーを行っているためです。それが置かれた参照は、フラッシュに存在するmemcpyアドレスにあります...消去される可能性があります。0x00000869

特定のコードは次のとおりです。

static setup_t last_setup;
last_setup = *((setup_t*)(bdt->addr));

どこsetup_tbdt->addr_ void*_ setup_t_ この行は への呼び出しを生成しますmemcpy

私の質問は、構造体のコピーを維持したいということです。それは便利です。memcpy をデフォルト以外の特定のセクションに配置するようにコンパイラに指定する方法はありますか? ブートローダーモジュールだけでそれが起こるようにしたい. 他のすべてのコードはそれを持つことができますmemcpy...内部にあるブートローダーモジュールの特別なコピーが欲しいだけです.bootloader

これが単純に不可能な場合は、ブートローダ全体をアセンブリに書き込むか (面白くない)、ブートローダを個別にコンパイルして、かなり長い 16 進数の文字列としてエンド プログラムに含めて実行します。 RAMにコピーした後の文字列。文字列ルートは壊れやすく、実装が難しいため、私にはあまり魅力的ではありません...他の提案もいただければ幸いです。

このモジュールのコンパイル行は次のとおりです。

arm-none-eabi-gcc -Wall -fno-common -mthumb -mcpu=cortex-m0plus -ffreestanding -fno-builtin -nodefaultlibs -nostdlib -O0 -c src/bootloader.c -o obj/bootloader.o

通常、最適化は になりますが-Os、私はそれを取り除こうとしていましたmemcpy... うまくいきませんでした。

また、私はこの質問を見てきましたが、問題は解決しませんでした。

4

1 に答える 1

0

私は試したことはありませんが、EXTERN()リンカー スクリプト ディレクティブを使用して newlibmemcpy()を 2 回強制的にロードすることから逃れることができます。

于 2015-01-23T07:39:38.377 に答える