c++ - org - Cómo carga Mapas DLL en Espacio de direcciones de proceso
mapwindows 5 (4)
Tengo curiosidad por saber cómo funciona la DLL de Maps del cargador en Process Address Space. Cómo cargador hace esa magia. El ejemplo es muy apreciado.
Gracias por adelantado.
¿Qué nivel de detalle estás buscando? En el nivel básico, todos los enlazadores dinámicos funcionan de la misma manera:
- Las bibliotecas dinámicas se compilan con código reubicable (utilizando saltos relativos en lugar de absolutos, por ejemplo).
- El vinculador encuentra un espacio vacío del tamaño adecuado en el mapa de memoria de la aplicación y lee el código del DLL y cualquier información estática en ese espacio.
- La biblioteca dinámica contiene una tabla de compensaciones al inicio de cada función exportada, y las llamadas a las funciones de la DLL en el programa cliente se revisan en tiempo de carga con una nueva dirección de destino, según dónde se cargó la biblioteca.
- La mayoría de los sistemas de enlaces dinámicos tienen algún sistema para establecer una dirección de base preferida para una biblioteca en particular. Si se carga una biblioteca en su dirección preferida, se puede omitir la reubicación en los pasos 2 y 3.
Si está realmente interesado, debería leer el libro Linkers and Loaders .
Suponiendo que esto se encuentre en Windows (lo que indica la DLL), es posible que desee leer la página de documentación de Microsoft Dynamic Linking en tiempo de ejecución . No especifica en detalle cómo se asigna la DLL en el espacio de direcciones; Supongo que no se supone que necesites saber eso.
De acuerdo, estoy asumiendo el lado Windows de las cosas aquí. Lo que sucede cuando carga un archivo PE es que el cargador (contenido en NTDLL) hará lo siguiente:
- Ubique cada una de las DLL usando la semántica de búsqueda de DLL (específica del sistema y del nivel de parche), las DLL bien conocidas están exentas de esto
- Asigne el archivo a la memoria (MMF), donde las páginas son de escritura por copia (CoW)
- Recorre el directorio de importación y para cada inicio de importación (recursivamente) en el punto 1.
- Resuelva las reubicaciones, que la mayoría de las veces es solo un número muy limitado de entidades, ya que el código en sí es un código independiente de posición (PIC)
- (IIRC) parche el EAT de RVA (dirección virtual relativa) a VA (dirección virtual dentro del espacio de memoria de proceso actual)
- Parchear el IAT (tabla de direcciones de importación) para hacer referencia a las importaciones con su dirección real dentro del espacio de memoria de proceso
- Para una DLL llamada
DLLMain()
para un EXE, cree un hilo cuya dirección de inicio esté en el punto de entrada del archivo PE (esto también está simplificado, porque la dirección de inicio real está dentro de kernel32.dll para los procesos de Win32)
Ahora cuando compila código, depende del vinculador cómo se hace referencia a la función externa. Algunos enlazadores crean stubs para que, en teoría, intentar verificar la dirección de la función contra NULL siempre diga que no es NULL. Es una peculiaridad que debes tener en cuenta si y cuándo se afecta tu enlazador. Otros hacen referencia a la entrada IAT directamente, en cuyo caso una dirección sin función (piense en DLL cargados con retraso) puede ser NULL y el manejador SEH invocará al auxiliar de carga de retardo y (intentará) resolver la dirección de la función, antes de reanudar la ejecución en el señalar que falló
Hay una gran cantidad de trámites burocráticos involucrados en el proceso anterior, que simplifiqué demasiado.
La esencia de lo que quería saber es que el mapeo en el proceso ocurre como un archivo MMF , aunque puede imitar artificialmente el comportamiento con el espacio del montón. Sin embargo, si recuerda el punto sobre CoW, ese es el quid en la idea de las DLL. En realidad, la misma copia de (la mayoría de) las páginas de la DLL se compartirá entre los procesos que cargan una DLL particular. Las páginas que no se comparten son las que escribimos, por ejemplo, cuando se resuelven traslados y cosas similares. En este caso, cada proceso tiene una copia, ahora modificada, de la página original.
Y una palabra de advertencia sobre los empaquetadores EXE en DLL. Derrotan exactamente este mecanismo CoW que describí en que asignan espacio para los contenidos desempaquetados de la DLL en el montón del proceso en el que se carga la DLL. Por lo tanto, aunque el contenido real del archivo aún se correlaciona como MMF y se comparte, los contenidos desempaquetados ocupan la misma cantidad de memoria para cada proceso que carga el archivo DLL en lugar de compartirlo.