Programación en Lenguaje Ensamblador

-El Verdadero Lenguaje de las Máquinas-

Alineación de memoria

–En Ensamblador lo que importa es el Ancho–

Antes de entrar con toda una serie sobre los arreglos y las estructuras en ensamblador es necesario abordar el tema de la alineación de memoria. Como ya saben la memoria de una computadora se puede representar como una enorme secuencia de celdas capaces de almacenar valores donde podemos referirnos a cada una de manera individual conociendo su posición. La arquitectura del sistema define que tan grandes son los números que estas celdas pueden almacenar y como el CPU intercambia información con ellas. Algunos sistemas solo pueden trabajar con celdas de un ancho predeterminado y otros pueden acceder a grupos de celdas en diferentes cantidades y usarlas como si fueran una sola. Esto nos recuerda un poco a lo que en otros lenguajes llaman tipos de datos. Sin embargo en ASM el contenido de una celda de memoria no tiene un significado tan estricto como en otros lenguajes. Por ejemplo una celda de 32 bits (dividida en 4 bytes) puede contener una palabra de 4 letras en ASCII, un valor entero que puede o no puede tener signo, un valor de punto flotante de presición sencilla o bien puede almacenar una posición de memoria. Esa cosa que algunos llaman puntero. Por ahora no nos importa lo que el contenido signifique sino solo el hecho de que es un dato de 32 bits representado por 4 celdas continuas de 8 bits cada una. A continuación les dejo uno de mis tradicionales dibujitos en paint donde se muestran los anchos de datos mas comunes  en un procesador intel.

anchos

Algunos comentarios sobre estos datos: Los valores de 8 bits pueden manipularse con las partes de 8 bits de los registros de propósito general EAX, ECX, EDX y EBX. Los datos de 16 bits aunque es posible usarlos en los procesadores nuevos esto no es nada eficiente y solo permanece para fines de compatibilidad pero no vale la pena usarlos a menos que estén programando un viejo procesador de 16 bits como cualquiera anterior al 80386 o si están experimentando con un emulador de DOS. Los valores de 32 bits son los valores de ancho por defecto en la mayoría de los CPU’s de 32 bits o compatibles y deben de ser usados lo mas posible en una de estas arquitecturas para alcanzar la mayor velocidad sin importar que parezca ser un desperdicio de espacio. Los valores de 64 bits son los que se usan en los nuevos CPU’s de 64 bits o arquitecturas de 32 bits extendidas, aunque existen instrucciones especiales para trabajar con valores de este ancho al menos desde la época de los procesadores con tecnología MMX y algunos procesadores AMD que operaban en modo de 64 bits. Por último están los valores de 128 bits de ancho que bien pueden ser usados de manera escalar o vectorial y son usados por el conjunto de instrucciones SIMD, incluso hay instrucciones SIMD capaces de utilizar mas de un registro de 128 bits para manejar datos vectoriales de precisión doble.

Ahora bien, los datos en la memoria no nos sirven para gran cosa si el CPU no puede tener acceso a ellos. Y es aquí donde comienza el problema. Existe algo que se llama alineación de datos y en pocas palabras significa que para poder mover un dato entre la RAM y el CPU este dato debe de estar almacenado en una localidad de memoria cuya posición sea un número exactamente divisible entre su ancho en bytes. Asi, los datos de un byte pueden almacenarse en cualquier celda, los datos de 16 bits (dos bytes) deben de estar en posiciones divisibles entre dos, los de 32 bits (4 bytes), en posiciones divisibles entre 4, los de 64 bits entre 8 y los de 128 bits en 16. Ahora, aunque muchos procesadores cumplen esta regla de manera estricta al grado de que si no se cumple no se permite el intercambio de datos este no es el caso de un procesador de intel, pues en estos si es posible leer datos que no estén alineados pero su acceso es mucho mas lento e ineficiente que si lo estuvieran. Aunque hay casos en los que de manera explícita podemos ordenarle al CPU que genere un error si el dato no está debidamente alineado como ocurre si usamos la instrucción MOVAPS.

Alineamiento de Datos en Memoria

¿Pero qué es lo que pasa si los datos no se encuentran alineados? No mucho, tan solo que el CPU va a necesitar muchos mas ciclos de acceso para poder usarlos y como ya han de saber si no lo he hecho ya la memoria es mucho mas lenta que el CPU y si además vamos a necesitar varios ciclos extra para mover la información el impacto en el desempeño va a ser muy grande. En el siguiente dibujo se muestran un grupo de datos debidamente alineados como se verían en un editor hexadecimal. Para saber si un dato está debidamente alineado basta ver que ninguna de las lineas verticales corte ningun rectángulo de su mismo color.

alignment

Ahora la pregunta que se han de estar haciendo es como demonios alineamos los datos. No hay una respuesta sencilla a esto. Una forma es comenzar los segmentos de datos en alguna posición cuyo último dígito hexadecimal sea cero y luego acomodar los datos del comenzando por el mas ancho y acabando por los mas angostos, si no hay datos raros como flotantes de precisión extendida (80 bits) o cadenas de texto de longitud variable esto organizará los datos de manera automática. En caso de que esto no sea posible siempre se puede rellenar el espacio de memoria con bytes que en realidad no sirven para otra cosa mas que para alinear los datos, a esta técnica se le llama “padding”. En el caso de estructuras dinámicas es posible asignar una posición alineada en tiempo de ejecución. Por último, FASM cuenta con una macro llamada ALIGN que recibe un argumento que indica el ancho al que se deben de alinear los datos y de manera automática hace un padding. Por cierto, la alineación de memoria para tener un acceso mucho mas veloz a los datos no solo sirve para los datos. ¡También funciona para el código! Es decir que si las instrucciones están debidamente alineadas con sus anchos se ejecutarán con mucha mayor rapidez. Pero alinear código es muchisimo mas dificil que alinear datos y aunque existen instrucciones NOP que pueden servir para hacer padding con los opcodes esto no siempre resulta tan eficiente. Aunque hay otras técnicas mas avanzadas para aumentar la velocidad de ejecución del código que no tienen tanto que ver con la alineación de memoria pero eso ya será tema para otra ocasión.

Bueno, supongo que ahora que saben lo que es la alineación de memoria y su impacto en la velocidad de acceso a los datos ya estarán listos para cuando veamos el tema de arreglos y estructuras en ASM, tema que comenzará a ser discutido a partir de la siguiente nota.

Anuncios

agosto 28, 2010 - Posted by | Uncategorized | ,

2 comentarios »

  1. Exelente post! Te felicito por la claridad con la que tratas el tema

    Comentario por emilio | junio 9, 2011 | Responder

  2. […] Resulta que el compilador mete su propia basura para poder alinear la memoria, aquí teneis la explicación for dummies   No quiero entrar en detalle sobre este aspecto, porque creo que @ShellShockLabs lo hará más en […]

    Pingback por Seguridad al Día » Buffer Overflow : The lazy way | diciembre 5, 2013 | Responder


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: