assembly - VA(Dirección virtual) y RVA(Dirección virtual relativa)
linker loader (2)
La mayoría de los procesos de Windows (* .exe) se cargan en la dirección de memoria (modo de usuario) 0x00400000, eso es lo que llamamos "dirección virtual" (VA), ya que solo son visibles para cada proceso y se convertirán en direcciones físicas diferentes por el sistema operativo (visible por la capa kernel / controlador).
Por ejemplo, una posible dirección de memoria física (visible por la CPU):
0x00300000 on physical memory has process A''s main
0x00500000 on physical memory has process B''s main
Y el sistema operativo puede tener una tabla de asignación:
process A''s 0x00400000 (VA) = physical address 0x00300000
process B''s 0x00400000 (VA) = physical address 0x00500000
Luego, cuando intente leer 0x004000000 en el proceso A, obtendrá el contenido que se encuentra en 0x00300000 de la memoria física.
En cuanto a RVA, está diseñado simplemente para facilitar la reubicación. Al cargar módulos reubicables (p. Ej., DLL), el sistema intentará deslizarlo por el espacio de la memoria de proceso. Entonces, en el diseño del archivo, coloca una dirección "relativa" para ayudar al cálculo.
Por ejemplo, una DLL C puede tener esta dirección:
RVA 0x00001000 DLL C''s main entry
Cuando se carga en el proceso A en la dirección base 0x10000000, la entrada principal de C se convierte
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).
Cuando se carga en el proceso B en la dirección base 0x32000000, la entrada principal de C se convierte
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).
Por lo general, el RVA en archivos de imagen es relativo a la dirección base del proceso cuando se carga en memoria, pero algunos RVA pueden ser relativos a la dirección de inicio de "sección" en archivos de imagen o objeto (debe verificar las especificaciones de formato PE para obtener más detalles). No importa cuál, RVA es relativo a "algunos" VA base.
Para resumir,
- La dirección de la memoria física es lo que ve la CPU
- Addreess virtual (VA) es relativo a la dirección física, por proceso (administrado por el sistema operativo)
- RVA es relativo a VA (base de archivo o base de sección), por archivo (administrado por el vinculador y el cargador)
(editar) sobre la nueva pregunta de garra:
El valor de RVA de un método / variable NO es siempre su desplazamiento desde el comienzo del archivo. Por lo general, son relativos a algunos VA, que pueden ser una dirección de base de carga predeterminada o una base de VA de sección; por eso le digo que debe verificar las especificaciones de formato PE para obtener más información.
Su herramienta, PEView está tratando de mostrar el RVA de cada byte para cargar la dirección base. Dado que las secciones comienzan en una base diferente, los RVA pueden ser diferentes al cruzar secciones.
En cuanto a sus suposiciones, están muy cerca de las respuestas correctas:
Por lo general, no discutiremos las secciones "RVA" antes, pero el encabezado PE aún se cargará hasta el final de los encabezados de las secciones. La brecha entre el encabezado de la sección y el cuerpo de la sección (si corresponde) no se cargará. Puedes examinar eso por depuradores. Además, cuando hay una brecha entre las secciones, es posible que no estén cargadas.
Como dije, RVA es simplemente "relativo a algunos VA", independientemente de qué VA sea (aunque cuando se habla de PE, el VA generalmente hace referencia a la dirección de la base de carga). Cuando lea la especificación de formato PE, puede encontrar algo de "RVA" que es relativo a alguna dirección especial, como la dirección de inicio del recurso. La lista PEView RVA de 0x1000 se debe a que esa sección comienza en 0x1000. ¿Por qué 0x1000? Debido a que el enlazador dejó 0x1000 bytes para el encabezado PE, entonces el RVA comienza en 0x1000.
Lo que te has perdido es el concepto de "sección" en la etapa de carga de PE. El PE puede contener varias "secciones", cada sección se asigna a una nueva dirección VA inicial. Por ejemplo, esto se descarta de 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
Hay un "0 encabezado invisible RVA = 0000, SIZE = 1000" que obliga a .text a comenzar en RVA 1000. Las secciones deben ser continuas cuando se cargan en la memoria (es decir, VA) para que su RVA sea continuo. Sin embargo, dado que la memoria está asignada por páginas, será múltiple de tamaño de página (4096 = 0x1000 bytes). Es por eso que la sección n. ° 2 comienza en 1000 + C5000 = C6000 (C5000 proviene de C44C1).
Para proporcionar mapeo de memoria, estas secciones aún deben estar alineadas por algún tamaño (tamaño de alineación de archivo - decidir por el enlazador. En mi ejemplo anterior es 0x200 = 512 bytes), que controla el campo PhysSize. Offset significa "desplazamiento al archivo físico PE comenzando".
Entonces, los encabezados ocupan 0x800 bytes de archivo (y 0x1000 cuando se asignan a la memoria), que es el desplazamiento de la sección # 1. Luego, al alinear sus datos (c44c1 bytes), obtenemos la forma física C4600. C4600 + 800 = C4E00, que es exactamente el desplazamiento de la segunda sección.
OK, esto está relacionado con cosas de carga de PE completas, así que puede ser un poco difícil de entender ...
(editar) déjame hacer un nuevo resumen simple de nuevo.
- Los archivos "RVA" en DLL / EXE (formato PE) generalmente son relativos a la "dirección base de carga en la memoria" (pero no siempre, debe leer la especificación)
- El Formato PE contiene una estructura de mapeo de "sección" para mapear el contenido del archivo físico en la memoria. Entonces, el RVA no es realmente relativo al offset del archivo.
- Para calcular un RVA de un byte, debe encontrar su desplazamiento en la sección y agregar la base de la sección.
Un archivo que se da como entrada al enlazador se llama Archivo de objeto . El enlazador produce un archivo de imagen , que a su vez es utilizado como entrada por el cargador.
Una propaganda de " Especificación de formato de archivo de objeto común y ejecutable portátil de Microsoft "
RVA (dirección virtual relativa) . En un archivo de imagen, la dirección de un elemento después de que se carga en la memoria, con la dirección base del archivo de imagen restada de él. El RVA de un elemento casi siempre difiere de su posición dentro del archivo en el disco (puntero del archivo).
En un archivo de objeto, un RVA es menos significativo porque las ubicaciones de memoria no están asignadas. En este caso, un RVA sería una dirección dentro de una sección (que se describe más adelante en esta tabla), a la que luego se aplica una reubicación durante la vinculación. Para simplificar, un compilador debería simplemente establecer el primer RVA en cada sección en cero.
VA (dirección virtual) . Igual que RVA, excepto que la dirección base del archivo de imagen no se resta. La dirección se llama "VA" porque Windows crea un espacio VA específico para cada proceso, independientemente de la memoria física. Para casi todos los propósitos, un VA debe considerarse solo una dirección. Un VA no es tan predecible como un RVA porque el cargador podría no cargar la imagen en su ubicación preferida.
Incluso después de leer esto, todavía no lo entiendo. Tengo muchas preguntas. ¿Alguien puede explicarlo de una manera práctica? Siga la terminología de Object File
de Object File
y Image File
como se indica.
Todo lo que sé sobre direcciones, es que
- Ni en el archivo de objeto ni en el archivo de imagen, no conocemos las ubicaciones de memoria exactas,
- El ensamblador al generar el archivo de objeto calcula las direcciones relativas a las secciones
.data
y.text
(para nombres de funciones). - El vinculador que toma varios archivos de objeto como entrada genera un archivo de imagen. Durante la generación, primero fusiona todas las secciones de cada archivo de objeto y, al fusionarlo, vuelve a calcular las compensaciones de dirección en relación con cada sección. Y no hay nada como las compensaciones globales.
Si hay algo mal en lo que sé, por favor corrígeme.
EDITAR:
Después de leer la respuesta dada a Francis, tengo claro qué es Dirección física, VA y RVA y cuál es la relación entre ellos.
Los RVA de todas las variables y métodos deben ser calculados por el Enlazador durante la reubicación. Entonces, (el valor de RVA de un método / variable) == (¿su desplazamiento desde el comienzo del archivo) ? debe ser verdad Pero sorprendentemente, no es así. ¿Porque?
Lo comprobé usando PEView en c:/WINDOWS/system32/kernel32.dll
y encontré que:
- RVA y FileOffset son iguales hasta el comienzo de las Secciones. (
.text
es la primera sección en este dll). - Desde el comienzo de
.text
hasta.data
,.rsrc
hasta el último byte de la última sección (.reloc
) RVA y FileOffset son diferentes. y también el RVA del primer byte de la primera sección se muestra "siempre" como0x1000
- Lo interesante es que los bytes de cada sección son continuos en FileOffset. Quiero decir que otra sección comienza en el siguiente byte del último byte de una sección. Pero si veo lo mismo en RVA, se trata de una gran brecha entre los RVA del último byte de una sección y el primer byte de la siguiente sección.
Mi conjetura:
Todos, los bytes de datos que estaban antes de la primera
.text
(.text
aquí) "realmente" no se cargan en el espacio VA del proceso, estos bytes de datos se utilizan para localizar y describir estas secciones. Se pueden llamar, "datos de metadatos".Ya que no están cargados en el espacio de proceso de VA. el uso del término RVA tampoco tiene sentido, esta es la razón por la cual
RVA == FileOffset
para estos bytes.Ya que,
- El término RVA es válido solo para aquellos bytes que serán realmente cargados en el espacio VA.
- los bytes de
.text
,.data
,.rsrc
,.reloc
son tales bytes. - En lugar de comenzar desde RVA 0x00000, el software PEView lo inicia desde
0x1000
.
No puedo entender por qué la tercera observación. No puedo explicar.
Una dirección virtual relativa es un desplazamiento de la dirección en la que se carga el archivo. Probablemente la forma más sencilla de obtener la idea es con un ejemplo. Supongamos que tiene un archivo (por ejemplo, una DLL) que se carga en la dirección 1000h. En ese archivo, tiene una variable en RVA 200h. En ese caso, el VA de esa variable (después de que el DLL esté mapeado en la memoria) es 1200h (es decir, la dirección base de 1000h de la DLL más 200h RVA (desplazamiento) a la variable.