web-dev-qa-db-ja.com

VA(仮想アドレス)&RVA(相対仮想アドレス)

リンカへの入力として与えられるファイルは、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(関数名)に関連するアドレスを計算します。
  • 複数のオブジェクトファイルを入力として取るリンカーは、1つの画像ファイルを生成します。生成中、最初に各オブジェクトファイルのすべてのセクションをマージし、マージ中に各セクションに関連するアドレスオフセットを再計算します。また、グローバルオフセットのようなものはありません。

私が知っていることで何か間違っていることがあれば、私を修正してください。

編集:

フランシスに与えられた答えを読んだ後、私は、物理アドレス、VA&RVA、およびそれらの間の関係は何であるかについて明確です。

すべての変数とメソッドのRVAは、再配置中にリンカーによって計算される必要があります。したがって、(メソッド/変数のRVAの値)==(ファイルの先頭からのオフセット)?真実でなければなりません。しかし、驚くべきことに、そうではありません。なぜそうなのか?

c:\WINDOWS\system32\kernel32.dllPEView を使用してこれを確認したところ、次のことがわかりました。

  1. RVAとFileOffsetは、セクションの先頭まで同じです(.textはこのdllの最初のセクションです)。
  2. .textの先頭から.data.rsrcの先頭から最後のセクションの最後のバイト(.reloc)までRVAとFileOffsetは異なります。 &また、最初のセクションの最初のバイトのRVAは「常に」0x1000として表示されます
  3. 興味深いのは、各セクションのバイトがFileOffsetで連続していることです。別のセクションは、セクションの最後のバイトの次のバイトから始まります。しかし、RVAで同じことを見ると、これらはセクションの最後のバイトのRVAと次のセクションの最初のバイトの間に大きなギャップがあります。

私の推測:

  1. すべて、最初の(.textここ)セクションの前にあったデータのバイトは、プロセスのVAスペースに実際にはロードされません。これらのデータのバイトは、これらのセクションを見つけて記述します。「メタセクションデータ」と呼ぶことができます。

    これらはプロセスのVAスペースにロードされません。RVAという用語の使用も無意味です。これらのバイトがRVA == FileOffsetである理由はこれです。

  2. 以来、

    • RVA用語は、VAスペースに実際にロードされるバイトのみに有効です。
    • .text.data.rsrc.relocのバイトはそのようなバイトです。
    • RVA 0x00000から開始する代わりに、PEViewソフトウェアは0x1000から開始します。
  3. なぜ3回目の観測なのか理解できません。私は説明できません。

47
claws

ほとんどの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に相対的です。

要約する、

  1. 物理メモリアドレスはCPUが見るものです
  2. Virtual Addreess(VA)は、プロセスごとの物理アドレスに相対的です(OSによって管理されます)
  3. RVAは、VA(ファイルベースまたはセクションベース)、ファイルごと(リンカーとローダーによって管理される)

(編集)爪の新しい質問に関して:

メソッド/変数のRVAの値は、常にファイルの先頭からのオフセットではありません。それらは通常、デフォルトのロードベースアドレスまたはセクションベースである可能性があるいくつかのVAに関連していますVA-だから、詳細については、 PEフォーマット仕様 を確認する必要があると言います。

ツールPEViewは、ベースアドレスをロードするためにすべてのバイトのRVAを表示しようとしています。セクションは異なるベースで始まるため、セクションを横断するときにRVAが異なる場合があります。

あなたの推測に関して、彼らは正しい答えに非常に近い:

  1. 通常、セクションの前に「RVA」については説明しませんが、PEヘッダーはセクションヘッダーの終わりまでロードされます。セクションヘッダーとセクションボディ(存在する場合)間のギャップは読み込まれません。デバッガーで調べることができます。さらに、セクション間にギャップがある場合、ロードされない場合があります。

  2. 私が言ったように、RVAは単純に「あるVAに対して」であり、VAに関係なく(PEについて話すとき、VAは通常、 t PEフォーマット仕様を読むと、リソース開始アドレスなどの特別なアドレスに関連する「RVA」が見つかる場合があります。0x1000からのPEViewリストRVAは、そのセクションが0x1000で始まるためです。リンカはPEヘッダー用に0x1000バイトを残したため、RVAは0x1000から始まります。

  3. あなたが見逃したのは、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のロード全体に関連しているため、理解するのが少し難しいかもしれません.

(編集)新しい簡単な要約をもう一度作りましょう。

  1. DLL/EXE(PE形式)ファイルの「RVA」は、通常「メモリ内のロードベースアドレス」に関連しています(ただし、常にではありません-仕様を読む必要があります)
  2. PE形式には、物理​​ファイルの内容をメモリにマッピングするための「セクション」マッピング構造が含まれています。そのため、RVAは実際にはファイルオフセットに対して相対的ではありません。
  3. あるバイトのRVAを計算するには、セクションでオフセットを見つけて、セクションベースを追加する必要があります。
62
Francis

相対仮想アドレスは、ファイルがロードされるアドレスからのオフセットです。おそらく、アイデアを得る最も簡単な方法は例です。アドレス1000hにロードされているファイル(DLLなど)があるとします。そのファイルには、RVA 200hに変数があります。その場合、その変数のVA(DLLがメモリにマップされた後)は1200h(つまり、DLLに200h RVA(オフセット)を加えた変数。

11
Jerry Coffin