アセンブラーを勉強しているときに、これらの用語に出くわしました。私が得たアイデアは次のようなものです。リロケータブル マシン コードでは、コードは静的な RAM の場所に依存しません。アセンブラは、プログラムに必要な RAM を指定します。メモリは、リンカがスペースを見つける場所ならどこにでも配置できます。
その考えは正しいですか?もしそうなら、それはアセンブラによってどのように行われますか?
また、アブソリュート マシン コードの例をいくつか教えてください。
アセンブラーを勉強しているときに、これらの用語に出くわしました。私が得たアイデアは次のようなものです。リロケータブル マシン コードでは、コードは静的な RAM の場所に依存しません。アセンブラは、プログラムに必要な RAM を指定します。メモリは、リンカがスペースを見つける場所ならどこにでも配置できます。
その考えは正しいですか?もしそうなら、それはアセンブラによってどのように行われますか?
また、アブソリュート マシン コードの例をいくつか教えてください。
多く/ほとんどの命令セットにはPC相対アドレッシングがあります。つまり、実行中の命令のアドレスに関連するプログラムカウンターのアドレスを取得し、それにオフセットを追加して、メモリへのアクセスや分岐などに使用します。それ。それはあなたが再配置可能と呼んでいるものです。その命令がアドレス空間のどこにあるかに関係なく、ジャンプ先は相対的だからです。コードとデータのブロック全体を他のアドレスに移動しても、それらは比較的同じ距離にあるため、相対アドレス指定は引き続き機能します。等しいスキップの場合、次の命令は、これら 3 つの命令 (if スキップ、スキップされる命令、スキップされる命令) がどこにあっても機能します。
絶対は絶対アドレスを使用し、この正確なアドレスにジャンプし、この正確なアドレスから読み取ります。等しい場合は、0x1000 に分岐します。
アセンブラーはこれを行いません。コンパイラーおよび/またはプログラマーが行います。一般に、コンパイルされたコードは最終的に絶対アドレス指定になります。特に、コードが互いにリンクされた個別のオブジェクトで構成されている場合は特にそうです。コンパイル時に、コンパイラはオブジェクトがどこに到達するかを知ることができず、外部参照がどこにあるのか、またはどれだけ離れているのかを知ることもできないため、一般に、それらがPC相対アドレス指定に十分近いと想定することはできません(通常、範囲制限があります)。 . そのため、コンパイラは、リンカーが絶対アドレスを入力するためのプレースホルダーを生成することがよくあります。この外部アドレスの問題がどのように解決されるかは、操作と命令セット、およびその他の要因によって異なります。最終的には、プロジェクトのサイズに基づいて、リンカーは絶対アドレス指定になります。したがって、デフォルト以外は通常、位置に依存しないコードを生成するコマンド ライン オプションです。たとえば、PIC は、コンパイラがサポートするものである可能性があります。コンパイラとリンカーの両方が、これらのアイテムの位置を独立させるために追加の作業を行う必要があります。アセンブリ言語プログラマーはこれをすべて自分で行う必要があります。アセンブラーは通常、これに関与せず、生成するように指示した命令のマシンコードを作成するだけです。
novectors.s:
.globl _start
_start:
b reset
reset:
mov sp,#0xD8000000
bl notmain
ldr r0,=notmain
blx r0
hang: b hang
.globl dummy
dummy:
bx lr
こんにちはC
extern void dummy ( unsigned int );
int notmain ( void )
{
unsigned int ra;
for(ra=0;ra<1000;ra++) dummy(ra);
return(0);
}
memap (リンカー スクリプト) MEMORY { ram : ORIGIN = 0xD6000000, LENGTH = 0x4000 } SECTIONS { .text : { (.text ) } > ram } Makefile
ARMGNU = arm-none-eabi
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
all : hello_world.bin
clean :
rm -f *.o
rm -f *.bin
rm -f *.elf
rm -f *.list
novectors.o : novectors.s
$(ARMGNU)-as novectors.s -o novectors.o
hello.o : hello.c
$(ARMGNU)-gcc $(COPS) -c hello.c -o hello.o
hello_world.bin : memmap novectors.o hello.o
$(ARMGNU)-ld novectors.o hello.o -T memmap -o hello_world.elf
$(ARMGNU)-objdump -D hello_world.elf > hello_world.list
$(ARMGNU)-objcopy hello_world.elf -O binary hello_world.bin
hello_world.list (気になる部分)
Disassembly of section .text:
d6000000 <_start>:
d6000000: eaffffff b d6000004 <reset>
d6000004 <reset>:
d6000004: e3a0d336 mov sp, #-671088640 ; 0xd8000000
d6000008: eb000004 bl d6000020 <notmain>
d600000c: e59f0008 ldr r0, [pc, #8] ; d600001c <dummy+0x4>
d6000010: e12fff30 blx r0
d6000014 <hang>:
d6000014: eafffffe b d6000014 <hang>
d6000018 <dummy>:
d6000018: e12fff1e bx lr
d600001c: d6000020 strle r0, [r0], -r0, lsr #32
d6000020 <notmain>:
d6000020: e92d4010 push {r4, lr}
d6000024: e3a04000 mov r4, #0
d6000028: e1a00004 mov r0, r4
d600002c: e2844001 add r4, r4, #1
d6000030: ebfffff8 bl d6000018 <dummy>
d6000034: e3540ffa cmp r4, #1000 ; 0x3e8
d6000038: 1afffffa bne d6000028 <notmain+0x8>
d600003c: e3a00000 mov r0, #0
d6000040: e8bd4010 pop {r4, lr}
d6000044: e12fff1e bx lr
ここで示しているのは、位置に依存しない命令と位置に依存する命令の混合です。
たとえば、これらの 2 つの命令は、アセンブラーに .word スタイルのメモリ ロケーションを強制的に追加させるためのショートカットです。
ldr r0,=notmain
blx r0
0xD600001c がその場所です。
d600000c: e59f0008 ldr r0, [pc, #8] ; d600001c <dummy+0x4>
d6000010: e12fff30 blx r0
...
d600001c: d6000020 strle r0, [r0], -r0, lsr #32
また、絶対アドレスであるアドレス 0xD6000020 が入力されているため、そのコードが機能するためには関数 notmain がアドレス 0xD6000020 にある必要があり、再配置できません。ただし、例のこの部分では、位置に依存しないコードも示しています。
ldr r0, [pc, #8]
私が話していたPC相対アドレッシングですこの命令セットの仕組みは、実行時にPCが2命令進んでいるか、基本的にこの場合、命令がメモリ内の0xD600000cにある場合、PCは実行時に0xD6000014になり、追加されます8 命令が示すように、0xD600001C を取得します。しかし、まったく同じマシンコード命令をアドレス 0x1000に移動し、さらに、読み取っているもの ( 0xD6000020 ) を含む周囲のすべてのバイナリをそこに移動するとします。基本的にこれを行います:
1000: e59f0008 ldr r0, [pc, #8]
1004: e12fff30 blx r0
...
1010: d6000020
そして、そのマシンコードは引き続き機能し、再アセンブルまたは再リンクする必要はありません。0xD6000020 コードは、ldr pc と blx がその固定アドレス ビットにある必要があります。
逆アセンブラはこれらを 0xd6... ベースのアドレスで表示しますが、bl と bne も PC 相対であり、命令セットのドキュメントを参照することで確認できます。
d6000030: ebfffff8 bl d6000018 <dummy>
d6000034: e3540ffa cmp r4, #1000 ; 0x3e8
d6000038: 1afffffa bne d6000028 <notmain+0x8>
0xD6000030 は、実行時に 0xD6000038 の pc を持ち、8 命令である 0xD6000038-0xD6000018 = 0x20 になります。そして、2 の補数の負の 8 は 0xFFF..FFFF8 です。そのマシン コード ebfffff8 の大部分は ffff8 であることがわかります。これは、符号が拡張され、プログラム カウンターに追加されて、基本的に後方 8 命令を意味します。同じことが 1afffffa の ffffa にも当てはまり、等しくない場合は 6 命令逆方向に分岐することを意味します。この命令セット (アーム) は、PC が 2 命令先にあると想定していることを思い出してください。
を削除すると、
d600000c: e59f0008 ldr r0, [pc, #8] ; d600001c <dummy+0x4>
d6000010: e12fff30 blx r0
その後、このプログラム全体が位置に依存しないことになりますが、たまたまそうなることはわかっていましたが、ツールにそうするように指示したからではなく、単にすべてを近づけて絶対アドレス指定を使用しなかったためです。
最後に、「リンカーがそれらのためのスペースを見つけた場所ならどこでも」と言ったときに、リンカースクリプトで0xD6000000から始まるすべてを配置するようリンカーに指示した場合、ファイル名または関数を指定しなかったため、特に指示されていない場合、このリンカーはアイテムを配置しますコマンドラインで指定された順序で。hello.c コードは 2 番目であるため、リンカが novectors.s コードを配置した後、リンカがその直後に余裕を持っていれば、hello.c コードは 0xD6000020 から始まります。
また、各命令を調査することなく、位置に依存しないものとそうでないものを簡単に確認するには、リンカ スクリプトを変更してコードを別のアドレスに配置します。
MEMORY
{
ram : ORIGIN = 0x1000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > ram
}
そして、もしあればどのマシンコードが変更され、何が変更されないかを確認します。
00001000 <_start>:
1000: eaffffff b 1004 <reset>
00001004 <reset>:
1004: e3a0d336 mov sp, #-671088640 ; 0xd8000000
1008: eb000004 bl 1020 <notmain>
100c: e59f0008 ldr r0, [pc, #8] ; 101c <dummy+0x4>
1010: e12fff30 blx r0
00001014 <hang>:
1014: eafffffe b 1014 <hang>
00001018 <dummy>:
1018: e12fff1e bx lr
101c: 00001020 andeq r1, r0, r0, lsr #32
00001020 <notmain>:
1020: e92d4010 push {r4, lr}
1024: e3a04000 mov r4, #0
1028: e1a00004 mov r0, r4
102c: e2844001 add r4, r4, #1
1030: ebfffff8 bl 1018 <dummy>
1034: e3540ffa cmp r4, #1000 ; 0x3e8
1038: 1afffffa bne 1028 <notmain+0x8>
103c: e3a00000 mov r0, #0
1040: e8bd4010 pop {r4, lr}
1044: e12fff1e bx lr
基本的に、「絶対」モードは、コードと RAM 変数がアセンブラーに指示した場所に正確に配置されることを意味し、「再配置可能」とは、アセンブラーがコード チャンクを構築し、リンカーが場所を見つけた場所に配置できる RAM の必要性を指定することを意味します。彼ら。