VA (виртуальный адрес) и RVA (относительный виртуальный адрес) - PullRequest
47 голосов
/ 31 января 2010

Файл, указанный в качестве входных данных для компоновщика, называется Объектный файл . Компоновщик создает файл изображения , который, в свою очередь, используется как ввод загрузчиком.

Рекламное объявление от " Спецификация формата исполняемого файла Microsoft Common и файла общего объекта "

RVA (относительный виртуальный адрес) . В файле изображения адрес элемента после загрузки в память базовый адрес файла изображения вычесть из него. RVA предмета почти всегда отличается от его положение в файле на диске (файл указатель).

В объектном файле RVA меньше смысл, потому что места памяти не назначены. В этом случае RVA будет адрес в разделе (описано далее в этой таблице), чтобы который позднее применяется во время ссылки. Для простоты компилятор должен просто установить первый RVA в каждом разделе до нуля.

VA (виртуальный адрес) . То же, что RVA, за исключением того, что базовый адрес файл изображения не вычитается. адрес называется «VA», потому что Windows создает отличное пространство VA для каждого процесса, независимо от физическая память. Почти для всех В целях просто адрес. ВА не так предсказуемо как RVA, потому что загрузчик может не загрузить изображение на предпочтительное местоположение.

Даже после прочтения я все равно не понимаю. У меня много вопросов. Может ли кто-нибудь объяснить это на практике. Пожалуйста, придерживайтесь терминологии Object File & Image File, как указано.

Все, что я знаю об адресах, это

  • Ни в объектном файле, ни в файле изображения мы не знаем точных областей памяти, поэтому
  • Ассемблер при генерации объектного файла вычисляет адреса относительно секций .data & .text (для имен функций).
  • Линкер, принимающий несколько объектных файлов в качестве входных данных, генерирует один файл изображения. При создании он сначала объединяет все разделы каждого объектного файла, а при объединении снова вычисляет смещения адресов относительно каждого раздела. И нет ничего лучше, чем глобальные смещения.

Если в том, что я знаю, что-то не так, поправьте меня.

EDIT:

После прочтения ответа, данного Фрэнсисом, я понимаю, что такое физический адрес, VA & RVA и какова связь между ними.

RVA всех переменных и методов должны быть вычислены компоновщиком во время перемещения. Итак, (значение RVA метода / переменной) == (его смещение от начала файла) ? должно быть правдой. Но удивительно, это не так. Почему так?

Я проверил это с помощью PEView на c:\WINDOWS\system32\kernel32.dll и обнаружил, что:

  1. RVA и FileOffset одинаковы до начала разделов (.text - первый раздел в этой dll).
  2. С начала .text до .data, .rsrc до последнего байта последнего раздела (.reloc) RVA и FileOffset различаются. а также RVA первого байта первого раздела «всегда» отображается как 0x1000
  3. Интересно то, что байты каждого раздела являются непрерывными в FileOffset. Я имею в виду, что другой раздел начинается со следующего байта последнего байта раздела. Но если я вижу то же самое в RVA, это огромный разрыв между RVA последнего байта раздела и первым байтом следующего раздела.

Угадай:

  1. Все байты данных, которые были до первого (.text здесь) раздел "не" фактически загружен в пространство процесса, эти байты данных просто используются для найдите и опишите эти разделы. Их можно назвать «мета-секцией» данные ".

    Так как они не загружаются в VA пространство процесса. использование Термин RVA также не имеет смысла, это причина, по которой RVA == FileOffset для этих байтов.

  2. С,

    • Термин RVA действителен только для тех байтов, которые будут фактически загружены в пространство VA.
    • байты .text, .data, .rsrc, .reloc являются такими байтами.
    • Вместо запуска с RVA 0x00000 Запуск программного обеспечения PEView это от 0x1000.
  3. Я не могу понять, почему 3-е наблюдение. Я не могу объяснить.

Ответы [ 2 ]

62 голосов
/ 01 февраля 2010

Большинство процессов Windows (* .exe) загружаются в (пользовательский режим) адрес памяти 0x00400000, это то, что мы называем «виртуальным адресом» (VA) - потому что они видны только каждому процессу и будут преобразованы в разные физические адреса ОС (видимые на уровне ядра / драйвера).

Например, возможный адрес физической памяти (видимый ЦП):

0x00300000 on physical memory has process A's main
0x00500000 on physical memory has process B's main

И ОС может иметь таблицу сопоставления:

process A's 0x00400000 (VA) = physical address 0x00300000
process B's 0x00400000 (VA) = physical address 0x00500000

Затем, когда вы попытаетесь прочитать 0x004000000 в процессе A, вы получите содержимое, которое находится в 0x00300000 физической памяти.

Что касается RVA, он просто разработан, чтобы облегчить перемещение. При загрузке перемещаемых модулей (например, DLL) система попытается вставить его в область памяти процесса. Таким образом, в макете файла он помещает «относительный» адрес, чтобы помочь вычислению.

Например, DLL C может иметь этот адрес:

 RVA 0x00001000 DLL C's main entry

При загрузке в процесс A по базовому адресу 0x10000000 основной записью 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).

При загрузке в процесс B по базовому адресу 0x32000000 основной записью 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. Адрес физической памяти - это то, что процессор видит
  2. Виртуальный адрес (VA) по отношению к физическому адресу, на процесс (управляемый ОС)
  3. RVA относительно VA (базы файлов или разделов) для каждого файла (управляется компоновщиком и загрузчиком)

(редактировать) относительно нового вопроса когтя:

Значение RVA метода / переменной НЕ всегда является его смещением от начала файла. Они обычно относятся к некоторому виртуальному компьютеру, который может быть базовым адресом загрузки по умолчанию или базовым виртуальным компьютером раздела - поэтому я говорю, что вы должны проверить спецификацию PE для получения подробной информации.

Ваш инструмент PEView пытается отобразить RVA каждого байта для загрузки базового адреса. Поскольку секции начинаются на разных основаниях, RVA может стать другой при пересечении секций.

Что касается ваших догадок, они очень близки к правильным ответам:

  1. Обычно мы не будем обсуждать «RVA» перед разделами, но заголовок PE будет по-прежнему загружаться до конца заголовков разделов. Разрыв между заголовком и телом раздела (если есть) не будет загружен. Вы можете проверить это отладчиками. Более того, когда между разделами есть некоторый промежуток, они могут быть не загружены.

  2. Как я уже сказал, RVA просто «относительно некоторого VA», независимо от того, какой это VA (хотя, говоря о PE, VA обычно ссылается на базовый адрес нагрузки). Когда вы читаете спецификацию формата PE, вы можете найти «RVA», который относится к какому-то специальному адресу, такому как начальный адрес ресурса. RVA списка PEView от 0x1000 потому, что этот раздел начинается с 0x1000. Почему 0x1000? Поскольку компоновщик оставил 0x1000 байт для PE-заголовка, 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 RVA = 0000, SIZE = 1000", который заставляет .text начинаться с RVA 1000. Секции должны быть непрерывными при загрузке в память (то есть, VA), поэтому их RVA является непрерывным. Однако, поскольку память распределяется по страницам, она будет кратна размеру страницы (4096 = 0x1000 байт). Вот почему секция № 2 начинается с 1000 + C5000 = C6000 (C5000 происходит от C44C1).

    Чтобы обеспечить отображение памяти, эти разделы должны быть выровнены по некоторому размеру (размер выравнивания файла - определяется компоновщиком. В моем примере выше это 0x200 = 512 байт), который контролирует поле PhysSize. Смещение означает «смещение к началу физического файла PE».

    Таким образом, заголовки занимают 0x800 байт файла (и 0x1000 при отображении в память), что является смещением раздела # 1. Затем, выровняв его данные (c44c1 байт), мы получим physsize C4600. C4600 + 800 = C4E00, что в точности соответствует смещению второй секции.

    Хорошо, это относится ко всей загрузке PE, так что это может быть немного трудно понять ...

(редактировать) позвольте мне снова сделать новое простое резюме.

* * 1068 «RVA» в файлах DLL / EXE (формат PE) обычно относится к «загрузить базовый адрес в память» (но не всегда - вы должны прочитать спецификацию) Формат PE содержит структуру отображения «раздел» для отображения физического содержимого файла в память. Таким образом, RVA на самом деле не относится к смещению файла. Чтобы вычислить RVA некоторого байта, вы должны найти его смещение в разделе и добавить базу раздела.
11 голосов
/ 31 января 2010

Относительный виртуальный адрес - это смещение от адреса, по которому загружается файл. Вероятно, самый простой способ получить идею - это на примере. Предположим, у вас есть файл (например, DLL), который загружен по адресу 1000h. В этом файле у вас есть переменная RVA 200h. В этом случае значение VA этой переменной (после сопоставления DLL с памятью) составляет 1200h (т.е. базовый адрес 1000h DLL, плюс 200h RVA (смещение) к переменной.

...