0

RAM内のU-bootの再配置を示すコードの一部を理解しようとしています。以下はコードです

   #ifndef  CONFIG_SKIP_RELOCATE_UBOOT
   relocate:
     adr r0,_start      /*r0 <--- Current posistion of code8*/
     ldr r1,_TEXT_BASE  /* test if we run from flash or Ram /*
     cmp r0,r1
     beq stack_setup
     ldr r2,_armboot_start   
     ldr r3,_bss_start
     sub r2,r3,r2
     add r2,r0,r2
   copy_loop:
     ldmia r0!,{r3-r10}
     stmia r1!,{r3-r10}
     cmp   r0,r2
     ble   cop_loop
     #endif /*CONFIG_SKIP_RELOCATE_UBOOT/*

ここでどのようになっているのか教えていただけないでしょうか??U-Boot が RAM またはフラッシュから実行されているかどうかをどのようにテストしていますか??

私はARMプラットフォームを使用しています。

4

1 に答える 1

2

これは非常に簡単な例です。

.globl _start
_start:

    adr r0,_start
    ldr r1,_TEXT_BASE

...

_TEXT_BASE: .word _start

組み立てて、リンクしてから分解すると、次のようになります。

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

そして、あなたの答えがあります。adr 命令は、実行時に PC に 0x8008 が含まれているという前提に基づいています。LDR は、どこにいても同じリンク時間値を取り込みます。

たとえば、このコードが実際にアドレス 0x20000000 にある場合、その最初の命令 (adr は疑似命令であり、逆アセンブリでは 8 のサブです)、adr が実行されると、0x20000008-8 = 0x20000000 が取得され、それを 0x8000 と比較すると、一致しません。コードを 0x8000 で実行している場合、0x8008-8 = 0x8000 となり、2 つが一致します。

コードを読んでadr命令を調べてください(または、私がしたことをして、コンパイラ/ツールの出力を調べて、答えが表示されない場合はハードウェアで実行してください)。

編集:

さまざまな接頭辞を持つ gnu ツールを使用しますが、このコードはほとんど気にしないほど単純です。arm-none-eabi- または arm-none-linux-gnueabi-。

アセンブリ ファイル名が foo.s であると仮定します。

arm-none-eabi-as foo.s -o foo.o
arm-none-eabi-ld -T memmap foo.o -o foo.elf
arm-none-eabi-objdump -D foo.elf

私が memmap と呼んでいるものはリンカー スクリプトです。この場合、コマンド ライン -Ttext=0x8000 を使用できます。また、クロス コンパイル バイナリに .elf 拡張子を使用するのが好きですが、誰もがそうしているわけではありません。

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

_start は、リンカがこのラベルにコードのエントリ ポイントがどこにあるかを知る必要がある/必要とする gnu ツールのものです。したがって、ubootも気にするかもしれませんが、実際にはubootのことではありませんが、間違いなくgnuリンカのことです。

0x8000 は、エントリ ポイントとしての ARM ベースの Linux プログラムの珍しいアドレスではありません。Linux は、そのようなアドレスで開始されたカーネルとして表示されますが、システムとバイナリを何にでもセットアップできるのは本当に任意です。

ここで何が起こっているかについて、実際にはARM固有または魔法は何もありません。適切な指示を考え出さなければならないプラットフォームに関係なく、同じ考えです。

アーム内のプログラム カウンターは 2 命令先です。これは 32 ビット アーム命令であるため、命令を実行するときのプログラム カウンターは、この命令のアドレス + 8 であると想定されます。この adr は _start の最初の命令であるため、これはこれを意味します命令は、リンク/コンパイルされたアドレス 0x8000 にあり、0x8008 から 0x8000 に到達する結果として、命令は r0 = pc-8 としてエンコードされます。その他の情報は、リンカがラベル _TEXT_BASE の後に _start のアドレスを提供することです。したがって、もう 1 つのステップは、その値を r1 にロードすることです。

これは、プロセッサがアドレス 0x8000 のフラッシュ内の命令を認識できるように、コードが実際にフラッシュ内に存在するという仮定と事実に基づいて操作する場合にのみ機能します。0x8000 と 0x8000 の比較は等しいので、プログラムは自分自身のコピーを RAM に作成し、このコピーがある場所の先頭にジャンプします。今回は、コードのコピーを通過するときに、1 つのレジスタにいくつかのアドレスが含まれます。 0x8000 以外なので比較は失敗します。フラッシュから元のバージョンを実行している場合、または RAM からコピーを実行している場合、これは単なる検出器です。目的は、コピーを作成し、RAM ベースのコピーを実行することです。コードが両方のアドレスで実行できることを保証するには、他の予防措置が必要です (位置に依存しないコード)。

たまたまフラッシュ アドレスが 0x00008000 であることがわかっていて、たとえば RAM アドレスが 0x20000000 であることがわかっている場合は、代わりに pc を 0x10000 と比較するか、pc の値を 0xFF000000 と比較することができます。

and r0,pc,#0xFF000000
beq stack_setup

しかし、これはより一般的なコードであると想定しているため、追加の命令と正確な比較が使用されています。

繰り返しますが、このタイプのトリックはかなり一般的であり、フラッシュまたは RAM コピーなどを検出します。使用される正確な指示と、リンカーに情報を入力させる方法は、ターゲットに固有です。

この場合、おそらくフラッシュはゼロ以外のアドレスにあり、0x8000 は RAM のコピーです。知らない。

于 2013-10-21T16:55:47.247 に答える