66

リンカへの入力として与えられるファイルは、オブジェクトファイルと呼ばれます。リンカは画像ファイルを生成し、それがローダーによる入力として使用されます。

MicrosoftPortableExecutableand Common ObjectFileFormatSpecification 」の宣伝文

RVA(相対仮想アドレス)。画像ファイルにおいて、メモリにロードされた後のアイテムのアドレス。画像ファイルのベースアドレスが差し引かれます。アイテムのRVAは、ほとんどの場合、ディスク上のファイル内の位置(ファイルポインター)とは異なります。

オブジェクトファイルでは、メモリ位置が割り当てられていないため、RVAはあまり意味がありません。この場合、RVAはセクション(この表の後半で説明)内のアドレスであり、後でリンク中に再配置が適用されます。簡単にするために、コンパイラは各セクションの最初のRVAをゼロに設定する必要があります。

VA(仮想アドレス)。イメージファイルのベースアドレスが減算されないことを除いて、RVAと同じです。Windowsは、物理メモリとは関係なく、プロセスごとに個別のVAスペースを作成するため、このアドレスは「VA」と呼ばれます。ほとんどすべての目的で、VAは単なるアドレスと見なす必要があります。ローダーがイメージを優先位置にロードしない可能性があるため、VAはRVAほど予測可能ではありません。

これを読んだ後でも、私はまだそれを理解していません。たくさん質問があります。誰でも実際的な方法でそれを説明できますか?Object File記載されているように&の用語に固執してくださいImage File

私が住所について知っているのはそれだけです

  • オブジェクトファイルでも画像ファイルでも、正確なメモリ位置がわからないので、
  • オブジェクトファイルの生成中のアセンブラは、セクション.data.text(関数名の場合)に関連するアドレスを計算します。
  • 複数のオブジェクトファイルを入力として受け取るリンカーは、1つの画像ファイルを生成します。生成中に、最初に各オブジェクトファイルのすべてのセクションをマージし、マージ中に、各セクションに関連するアドレスオフセットを再計算します。そして、グローバルオフセットのようなものはありません。

私が知っていることに何か問題がある場合は、私を訂正してください。

編集:

フランソワ1世の答えを読んだ後、私は物理アドレス、VA、RVAとは何か、そしてそれらの間の関係は何かについて明確になりました。

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

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

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

私の推測:

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

    プロセスのVAスペースにロードされていないため。RVA == FileOffsetRVAという用語の使用も無意味です。これがこれらのバイトの理由です。

  2. 以来、

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

4

2 に答える 2

84

ほとんどの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など)をロードする場合、システムはそれをプロセスメモリスペースにスライドさせようとします。したがって、ファイルレイアウトでは、計算に役立つ「相対」アドレスを配置します。

たとえば、DLLCのアドレスは次のとおりです。

 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. 仮想アドレス(VA)は、プロセスごとの物理アドレスに関連しています(OSによって管理されます)
  3. RVAは、ファイルごとのVA(ファイルベースまたはセクションベース)に関連しています(リンカーとローダーによって管理されます)

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

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

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

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

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

  2. 私が言ったように、RVAは、それがどんなVAであっても、単に「いくつかのVAに対して相対的」です(ただし、PEについて話すとき、VAは通常ロードベースアドレスを指します)。PE形式の仕様を読むと、リソースの開始アドレスなどの特別なアドレスに関連する「RVA」が見つかる場合があります。0x1000からのPEViewリストRVAは、そのセクションが0x1000で始まるためです。なぜ0x1000?リンカがPEヘッダーに0x1000バイトを残したため、RVAは0x1000から始まります。

  3. あなたが見逃したのは、PEローディング段階の「セクション」の概念です。PEには複数の「セクション」が含まれる場合があり、各セクションは新しい開始VAアドレスにマップされます。たとえば、これはwin7kernel32.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ヘッダー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バイト)を整列させることにより、physsizeC4600を取得します。C4600 + 800 = C4E00、これは正確に2番目のセクションのオフセットです。

    OK、これはPEの読み込み全体に関連しているので、理解するのは少し難しいかもしれません...

(編集)新しい簡単な要約をもう一度作成させてください。

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

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

于 2010-01-31T07:01:24.780 に答える