リンカへの入力として与えられるファイルは、Object Fileと呼ばれます。リンカはImage fileを生成し、これはローダーによる入力として使用されます。
「Microsoft Portable Executable and Common Object File Format Specification」からの宣伝文句
RVA(相対仮想アドレス)。画像ファイルにおいて、メモリにロードされた後のアイテムのアドレス。画像ファイルのベースアドレスから減算されます。アイテムのRVAは、ほとんどの場合、ディスク上のファイル内の位置(ファイルポインター)とは異なります。
オブジェクトファイルでは、メモリロケーションが割り当てられていないため、RVAはあまり意味がありません。この場合、RVAはセクション(この表で後述)内のアドレスになり、リンク中に後で再配置が適用されます。簡単にするために、コンパイラは各セクションの最初のRVAをゼロに設定するだけです。
VA(仮想アドレス)。 RVAと同じですが、画像ファイルのベースアドレスは減算されません。 Windowsは、物理メモリとは無関係に、プロセスごとに個別のVAスペースを作成します。ほとんどすべての目的で、VA VAはRVAほど予測可能ではありません。これは、ローダーがその優先位置にイメージをロードしない可能性があるためです。
これを読んだ後でさえ、私はまだそれを得ません。たくさん質問があります。誰でも実用的な方法で説明できますか。記載されているObject File
およびImage File
の用語に固執してください。
アドレスについて知っていることは、それだけです
.data
および.text
(関数名)に関連するアドレスを計算します。私が知っていることで何か間違っていることがあれば、私を修正してください。
編集:
フランシスに与えられた答えを読んだ後、私は、物理アドレス、VA&RVA、およびそれらの間の関係は何であるかについて明確です。
すべての変数とメソッドのRVAは、再配置中にリンカーによって計算される必要があります。したがって、(メソッド/変数のRVAの値)==(ファイルの先頭からのオフセット)?真実でなければなりません。しかし、驚くべきことに、そうではありません。なぜそうなのか?
c:\WINDOWS\system32\kernel32.dll
で PEView を使用してこれを確認したところ、次のことがわかりました。
.text
はこのdllの最初のセクションです)。.text
の先頭から.data
、.rsrc
の先頭から最後のセクションの最後のバイト(.reloc
)までRVAとFileOffsetは異なります。 &また、最初のセクションの最初のバイトのRVAは「常に」0x1000
として表示されます私の推測:
すべて、最初の(.text
ここ)セクションの前にあったデータのバイトは、プロセスのVAスペースに実際にはロードされません。これらのデータのバイトは、これらのセクションを見つけて記述します。「メタセクションデータ」と呼ぶことができます。
これらはプロセスのVAスペースにロードされません。RVAという用語の使用も無意味です。これらのバイトがRVA == FileOffset
である理由はこれです。
以来、
.text
、.data
、.rsrc
、.reloc
のバイトはそのようなバイトです。0x00000
から開始する代わりに、PEViewソフトウェアは0x1000
から開始します。なぜ3回目の観測なのか理解できません。私は説明できません。
ほとんどのWindowsプロセス(* .exe)は(ユーザーモード)メモリアドレス0x00400000に読み込まれます。これは「仮想アドレス」(VA)と呼ばれるものです。なぜなら、これらは各プロセスにのみ表示され、 OS(カーネル/ドライバー層から見える)。
たとえば、可能な物理メモリアドレス(CPUから見える):
0x00300000 on physical memory has process A's main
0x00500000 on physical memory has process B's main
また、OSにはマッピングテーブルがある場合があります。
process A's 0x00400000 (VA) = physical address 0x00300000
process B's 0x00400000 (VA) = physical address 0x00500000
次に、プロセスAで0x004000000を読み取ろうとすると、物理メモリの0x00300000にあるコンテンツを取得します。
RVAに関しては、再配置を容易にするために単純に設計されています。再配置可能なモジュール(DLLなど)をロードするとき、システムはプロセスメモリスペースを介してそれをスライドさせようとします。したがって、ファイルレイアウトでは、計算を支援するために「相対」アドレスを配置します。
たとえば、DLL Cには次のアドレスがあります。
RVA 0x00001000 DLL C's main entry
ベースアドレス0x10000000でプロセスAにロードされると、Cのメインエントリは
VA = 0x10000000 + 0x00001000 = 0x10001000
(if process A's VA 0x10000000 mapped to physical address was 0x30000000, then
C's main entry will be 0x30001000 for physical address).
ベースアドレス0x32000000でプロセスBにロードされると、Cのメインエントリは
VA = 0x32000000 + 0x00001000 = 0x32001000
(if process B's VA 0x32000000 mapped to physical address was 0x50000000, then
C's main entry will be 0x50001000 for physical address).
通常、画像ファイルのRVAはメモリにロードされるときのプロセスのベースアドレスに関連していますが、一部のRVAは画像またはオブジェクトファイルの「セクション」開始アドレスに関連する場合があります(詳細についてはPE形式の仕様を確認する必要があります)。いずれにせよ、RVAは「一部の」ベースVAに相対的です。
要約する、
(編集)爪の新しい質問に関して:
メソッド/変数のRVAの値は、常にファイルの先頭からのオフセットではありません。それらは通常、デフォルトのロードベースアドレスまたはセクションベースである可能性があるいくつかのVAに関連していますVA-だから、詳細については、 PEフォーマット仕様 を確認する必要があると言います。
ツールPEViewは、ベースアドレスをロードするためにすべてのバイトのRVAを表示しようとしています。セクションは異なるベースで始まるため、セクションを横断するときにRVAが異なる場合があります。
あなたの推測に関して、彼らは正しい答えに非常に近い:
通常、セクションの前に「RVA」については説明しませんが、PEヘッダーはセクションヘッダーの終わりまでロードされます。セクションヘッダーとセクションボディ(存在する場合)間のギャップは読み込まれません。デバッガーで調べることができます。さらに、セクション間にギャップがある場合、ロードされない場合があります。
私が言ったように、RVAは単純に「あるVAに対して」であり、VAに関係なく(PEについて話すとき、VAは通常、 t PEフォーマット仕様を読むと、リソース開始アドレスなどの特別なアドレスに関連する「RVA」が見つかる場合があります。0x1000からのPEViewリストRVAは、そのセクションが0x1000で始まるためです。リンカはPEヘッダー用に0x1000バイトを残したため、RVAは0x1000から始まります。
あなたが見逃したのは、PEローディング段階の「セクション」の概念です。 PEには複数の「セクション」が含まれ、各セクションは新しい開始VAアドレスにマップされます。たとえば、これはwin7 kernel32.dllからダンプされます。
# Name VirtSize RVA PhysSize Offset
1 .text 000C44C1 00001000 000C4600 00000800
2 .data 00000FEC 000C6000 00000E00 000C4E00
3 .rsrc 00000520 000C7000 00000600 000C5C00
4 .reloc 0000B098 000C8000 0000B200 000C6200
目に見えない「0 header RVA = 0000、SIZE = 1000」があり、.textをRVA 1000から開始しました。メモリ(つまりVA)にロードされるとき、RVAが連続するようにセクションは連続している必要があります。ただし、メモリはページごとに割り当てられるため、ページサイズの倍数になります(4096 = 0x1000バイト)。そのため、#2セクションは1000 + C5000 = C6000から始まります(C5000はC44C1から来ます)。
メモリマッピングを提供するために、これらのセクションは、PhysSizeフィールドを制御するサイズ(ファイルアラインメントサイズ-リンカが決定します。上の例では0x200 = 512バイト)でアラインする必要があります。オフセットとは、「物理PEファイルの先頭までのオフセット」を意味します。
そのため、ヘッダーはファイルの0x800バイト(およびメモリにマップされる場合は0x1000)を占有します。これはセクション#1のオフセットです。次に、データ(c44c1バイト)を調整することにより、physsize C4600を取得します。 C4600 + 800 = C4E00、これは正確に2番目のセクションのオフセットです。
OK、これはPEのロード全体に関連しているため、理解するのが少し難しいかもしれません.
(編集)新しい簡単な要約をもう一度作りましょう。
相対仮想アドレスは、ファイルがロードされるアドレスからのオフセットです。おそらく、アイデアを得る最も簡単な方法は例です。アドレス1000hにロードされているファイル(DLLなど)があるとします。そのファイルには、RVA 200hに変数があります。その場合、その変数のVA(DLLがメモリにマップされた後)は1200h(つまり、DLLに200h RVA(オフセット)を加えた変数。