x64 visual microsoft long descargar c++ c x86-64

long - microsoft visual c++ 2018 redistributable package(x64)



Comprobación de límites en hardware de 64 bits (2)

Estaba leyendo un blog en la edición de Firefox de 64 bits en hacks.mozilla.org .

El autor declara:

Para el código asm.js , el mayor espacio de direcciones también nos permite usar la protección de la memoria del hardware para eliminar de forma segura los controles de límites de los accesos del montón de asm.js Las ganancias son bastante dramáticas: 8% -17% en asmjs-apps - * - pruebas de rendimiento como se informa en arewefastyet.com .

Estaba tratando de entender cómo el hardware de 64 bits tiene una verificación automática de límites (suponiendo que el compilador tiene soporte de hardware) para C / C ++. No pude encontrar ninguna respuesta en SO. Encontré un documento técnico sobre este tema , pero no puedo comprender cómo se hace esto.

¿Puede alguien explicar las ayudas de hardware de 64 bits dentro de la verificación de límites?


La mayoría de las CPU modernas implementan direccionamiento virtual / memoria virtual: cuando un programa hace referencia a una dirección particular, esa dirección es virtual; la asignación a una página física, si corresponde, se implementa mediante la MMU (unidad de gestión de memoria) de la CPU. La CPU traduce cada dirección virtual a una dirección física buscándola en la tabla de páginas que configura el sistema operativo para el proceso actual. Estas búsquedas son almacenadas en caché por el TLB , por lo que la mayoría de las veces no hay un retraso adicional. (En algunos diseños de CPU que no son x86, las fallas TLB son manejadas en software por el sistema operativo).

Por lo tanto, mi programa accede a la dirección 0x8050, que está en la página virtual 8 (asumiendo el tamaño de página estándar de 4096 bytes (0x1000)). La CPU ve que la página virtual 8 está asignada a la página física 200 y, por lo tanto, realiza una lectura en la dirección física 200 * 4096 + 0x50 == 0xC8050 . (Al igual que TLB almacena en la tabla de páginas las búsquedas en caché, el L1 / L2 / L3 más familiariza los accesos de caché a la memoria RAM física).

¿Qué sucede cuando la CPU no tiene una asignación de TLB para esa dirección virtual? Tal cosa ocurre con frecuencia porque el TLB es de tamaño limitado. La respuesta es que la CPU genera un error de página , que es manejado por el sistema operativo.

Varios resultados pueden ocurrir como resultado de un error de página:

  • Uno, el sistema operativo puede decir "oh, bueno, simplemente no estaba en el TLB porque no podía encajar". El sistema operativo desaloja una entrada de la TLB y rellena en la nueva entrada utilizando el mapa de la tabla de páginas del proceso, y luego deja que el proceso siga funcionando. Esto sucede miles de veces por segundo en máquinas moderadamente cargadas. (En las CPU con hardware que TLB no puede manejar, como x86, este caso se maneja en hardware y no es ni siquiera un error de página "menor").
  • Dos, el sistema operativo puede decir "oh, bueno, esa página virtual no está asignada en este momento porque la página física que estaba usando se cambió a disco porque me quedé sin memoria". El sistema operativo suspende el proceso, encuentra algo de memoria para usar (tal vez intercambiando alguna otra asignación virtual), pone en cola una lectura de disco para la memoria física solicitada y, cuando se completa la lectura del disco, reanuda el proceso con la asignación de tabla de páginas recién rellenada. (Esto es un error de página "importante" .)
  • Tres, el proceso está tratando de acceder a la memoria para la que no existe una asignación; es una memoria de lectura que no debería ser. Esto se llama comúnmente una falla de segmentación.

El caso relevante es el número 3. Cuando ocurre un fallo de seguridad, el comportamiento predeterminado del sistema operativo es abortar el proceso y hacer cosas como escribir un archivo principal. Sin embargo, se permite que un proceso atrape sus propios errores de segregación e intente manejarlos, tal vez incluso sin detenerse. Aquí es donde las cosas se ponen interesantes.

Podemos usar esto para nuestra ventaja para realizar verificaciones de índice ''aceleradas por hardware'', pero hay algunos obstáculos más que intentamos hacer.

Primero, la idea general: para cada matriz, la colocamos en su propia región de memoria virtual, con todas las páginas que contienen los datos de la matriz asignados como de costumbre. A cada lado de la matriz real de datos, creamos asignaciones de páginas virtuales que son ilegibles y no se pueden escribir. Si intenta leer fuera de la matriz, generará un error de página. El compilador inserta su propio controlador de fallas de página cuando creó el programa, y ​​maneja la falla de la página, convirtiéndola en una excepción de índice fuera de límites.

El bloque de tropiezo número uno es que solo podemos marcar páginas enteras como legibles o no. Es posible que los tamaños de los arreglos no sean un múltiplo parejo de un tamaño de página, por lo que tenemos un problema: no podemos colocar cercos exactamente antes y después del final del arreglo. Lo mejor que podemos hacer es dejar un pequeño espacio, ya sea antes del comienzo de la matriz o después del final de la matriz entre la matriz y la página de ''valla'' más cercana.

¿Cómo se las arreglan para esto? Bueno, en el caso de Java, no es fácil compilar código que realice una indexación negativa; y si lo hace, no importa de todos modos porque el índice negativo se trata como si no estuviera firmado, lo que coloca al índice muy por delante del comienzo de la matriz, lo que significa que es muy probable que llegue a la memoria no asignada y que de todos modos provocará un error .

Entonces, lo que hacen es alinear la matriz de modo que el final de la matriz se alinee justo al final de la página, de esta forma (''-'' significa no mapeado, ''+'' significa mapeado):

-----------++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- | Page 1 | Page 2 | Page 3 | Page 4 | Page 5 | Page 6 | Page 7 | ... |----------------array---------------------------|

Ahora, si el índice está más allá del final de la matriz, llegará a la página 7, que no está asignada, lo que provocará un error en la página, que se convertirá en una excepción de índice fuera de límites. Si el índice está antes del comienzo de la matriz (es decir, es negativo), entonces, debido a que se trata como un valor sin signo, se volverá muy grande y positivo, lo que nos llevará más allá de la página 7 nuevamente, lo que provocará una lectura de memoria no asignada, lo que provocará un error de página, que de nuevo se convertirá en una excepción de índice fuera de límites.

El bloque de tropiezo número 2 es que realmente deberíamos dejar una gran cantidad de memoria virtual sin asignar más allá del final de la matriz antes de asignar el siguiente objeto, de lo contrario, si un índice estaba fuera de los límites, pero muy, muy, muy lejos de los límites, podría golpear una página válida y no causar una excepción de índice fuera de los límites, y en su lugar leería o escribiría una memoria arbitraria.

Para resolver esto, solo usamos grandes cantidades de memoria virtual: colocamos cada matriz en su propia región de 4 GiB de memoria, de la cual solo se asignan las primeras N páginas. Podemos hacer esto porque aquí solo estamos usando el espacio de direcciones , no la memoria física real. Un proceso de 64 bits tiene ~ 4 mil millones de partes de 4 regiones GiB de memoria, por lo que tenemos mucho espacio de direcciones para trabajar antes de que se agote. En una CPU o proceso de 32 bits, tenemos muy poco espacio de direcciones para jugar, por lo que esta técnica no es muy factible. Tal como está, muchos programas de 32 bits se están quedando sin espacio de direcciones virtuales simplemente tratando de acceder a la memoria real, sin importar el mapeo de páginas vacías ''de cerca'' para tratar de usarlas como verificaciones de rango de índice ''acelerado por hardware''.


La técnica que están utilizando es similar al modo de depuración de la pila de la página de Windows, solo que en lugar de una pila que pega cada VirtualAlloc() en su propia página de memoria virtual, este es un sistema que pega cada matriz (estática o basada en la pila) por sí misma. página de memoria virtual (más precisamente, coloca la asignación al final de la página, porque ejecutar el final de una matriz es mucho más común que tratar de acceder antes de comenzar); luego coloca una "página de protección" inaccesible después de la página de asignación, o incluso una cantidad considerable de páginas en su caso.

Con eso, los controles de límites no son un problema, porque un acceso fuera de los límites provocará una violación de acceso (SIGSEGV) en lugar de dañar la memoria. Esto no era posible en el hardware anterior simplemente porque una máquina de 32 bits solo tenía 1 millón de páginas para jugar, y eso no era suficiente para manejar una aplicación que no fuera un juguete.