1.12k likes | 1.5k Views
Programación en Lenguaje Ensamblador. Este material se refiere al compilador gcc (GNU compiler collection) el cual corre bajo ambiente Linux. El ensamblador de gcc se llama as y por formar parte de gcc comúnmente se conoce como gas. Programación en Lenguaje Ensamblador. Plataforma: IA-32
E N D
Programación en Lenguaje Ensamblador Este material se refiere al compilador gcc (GNU compiler collection) el cual corre bajo ambiente Linux. El ensamblador de gcc se llama as y por formar parte de gcc comúnmente se conoce como gas.
Programación en Lenguaje Ensamblador Plataforma: IA-32 IA-32 es la arquitectura de microprocesadores de 32 bits de Intel (Intel Architecture 32). Son los microprocesadores más usados en los ordenadores personales (PC). Esta gama de microprocesadores comenzó con el Intel 80386 en 1985, conocido luego popularmente como 386 o x86 para denominar a toda la gama. Los procesadores de Intel que siguieron y mantuvieron la compatibilidad son el 486, Pentium, Pentium II, Pentium III, Pentium 4, y la línea Intel Core. La novedad de estos procesadores con respecto a sus predecesores es que incluyen gestión de memoria avanzada (segmentación, paginación, soporte de memoria virtual), unidad de punto flotante, y a partir del Pentium MMX, soporte para operaciones matriciales complejas, muy usadas en aplicaciones gráficas y multimedia.
Plataforma: IA-32 Tipos de datos La información se puede accesar de diversas maneras. Se puede leer un sólo byte (8 bits) o un conjunto de bytes, en esta máquina en particular se denomina palabra a dos bytes y doble palabra a 4 bytes. La notación puede ser en decimal o en hexadecimal.
Plataforma: IA-32 Tamaños de los datos:
Plataforma: IA-32 Orden de los datos en memoria: En gas las instrucciones utilizan un sufijo para indicar el tamaño de los datos sobre los cuales operan. El sistema guarda los datos en memoria en secuencia inversa de bytes (little endian) lo cual trae como consecuencia que el byte menos significativo se ubica en la posición de menor orden y el byte más significativo en la posición de memoria de mayor orden. Por ejemplo si se transfiere el dato 0x457A a las posiciones consecutivas de memoria 0x100 y 0x101 se ubica el byte 7A en la posición 0x100 y el byte 45 en la posición 0x101.
Plataforma: IA-32 Registros de propósito general Los registros de propósito general se utilizan para almacenar datos temporalmente, debido a que estos registros han evolucionado desde una máquina de 8 bits (el 8080) un grupo de registros aún se puede acceder de 8 bits para mantener compatibilidad con toda la línea de procesadores. Aún cuando estos registros pueden mantener cualquier tipo de datos, algunos tienen cierta funcionalidad específica o son usados de manera especial por algunas instrucciones.
Plataforma: IA-32 Registros de propósito general
Plataforma: IA-32 Registros de propósito general En gas los registros se denotan usando el símbolo de porcentaje antes del nombre del registro. Los registros %eax, %ebx, %ecx y %edx pueden ser accesados con tamaños de 8, 16 o 32 bits cambiando su nomenclatura de acuerdo al tamaño. Ejemplo para %eax:
Plataforma: IA-32 Registros de propósito general Los registros %edi, %esi, %ebp y %esp se pueden accesar como registros de 16 o 32 bits. Ejemplo para %edi:
Plataforma: IA-32 Registro de instrucción: El registro de instrucción o contador de programa contiene la dirección de la próxima instrucción a ejecutarse. Registros de punto flotante: Son 8 registros los cuales son tratados como una pila. Se nombran %st(0), %st(1), %st(2), etc. %st(0) se ubica en el tope de la pila. Banderas: Proveen una manera de obtener información acerca del estado actual de la máquina y el resultado de procesamiento de una instrucción. La plataforma IA-32 utiliza un registro de 32 bits llamado EFLAGS que contiene las banderas.
Plataforma: IA-32 Banderas: Estas son las banderas más comunes:
Plataforma: IA-32 Banderas: • La bandera de acarreo se activa cuando se produce acarreo en una operación matemática entre números sin signo. • La bandera de paridad se usa para indicar si el resultado, en un registro, de una operación matemática es válido. • La bandera de ajuste se utiliza en operaciones matemáticas con números decimales codificados en binario (BCD). Se activa si hay acarreo presente. • La bandera de cero se activa si el resultado de una operación es cero. • La bandera de signo muestra el bit más significativo del resultado de una operación, el cual denota el signo del número.
Plataforma: IA-32 Banderas: • La bandera de dirección controla la selección de autoincremento o autodecremento de los registros %edi o %esi durante las operaciones con cadenas de caracteres. • La bandera de dirección sólo se utiliza con las instrucciones para el manejo de cadenas de caracteres. • La bandera de desbordamiento se utiliza en la aritmética de enteros con signo cuando un número sobrepasa la capacidad de representación del registro.
Programación en ensamblador Un programa en lenguaje ensamblador está orientado a líneas y cada enunciado especifica una operación sencilla. Conceptos básicos de los programas en lenguaje ensamblador: • Espacio: Un espacio es equivalente a cualquier número de espacios o tabuladores. Los espacios no pueden aparecer en medio de un número o identificador. • Comentario: Texto que aparece después de un carácter de inicio de comentario, el ensamblador ignora los comentarios. Los comentarios comienzan con el símbolo #.
Programación en ensamblador Conceptos básicos de los programas en lenguaje ensamblador: • Identificador: Es una letra seguida de cualquier cantidad de letras o dígitos (y en algunos casos caracteres especiales). • Etiqueta: identificador seguido de dos puntos. • Instrucción: es una operación seguida de una lista de operandos. Debe haber un espacio entre el nombre de la operación y la lista de operandos. Los operandos se separan por comas, el número de operandos en la lista depende de la operación.
Programación en ensamblador Conceptos básicos de los programas en lenguaje ensamblador: • Directriz: consiste en un nombre de directriz seguido de una lista de parámetros. Los nombres de la directrices comienzan con un punto. Con las directrices se especifica la forma en que el ensamblador traduce las instrucciones, es decir, las directrices dirigen el proceso de traducción.
Programación en ensamblador El programa escrito en lenguaje ensamblador se compone de varias secciones. Las secciones más comunes son: • sección de texto • sección de datos • sección bss. En la sección de texto se escriben las instrucciones, en la sección de datos los datos inicializados y en la sección bss las variables sin inicializar. Cada una de las secciones se declara por medio de una directiva.
Programación en ensamblador Para declarar las secciones mencionadas se usan las siguientes directivas: .section .text para la sección de texto .section .data para la sección de datos .section .bss para la sección bss Comúnmente las secciones se colocan en la siguiente secuencia: .section .data .section .bss .section .text bss son las siglas correspondientes a "block storage start", que significa inicio de bloque de almacenaje.
Programación en ensamblador Punto de inicio de programa: se define por medio de la declaración de una etiqueta: _start la cual indica a partir de qué instrucción se comienza a ejecutar el código. Esta etiqueta debe ser declarada como global, es decir, que esté disponible para aplicaciones externas; esto se logra utilizando la directiva .globl.
Programación en ensamblador Finalización del programa: Gas no provee una instrucción de fin de ejecución, esto se logra mediante una llamada al sistema. Para realizar esta llamada se pasa dos parámetros: El valor 1 en el registro %eax indica el código de llamada a exit (salida). El valor 0 en el registro %ebx indica la salida normal del programa.
Programación en ensamblador Estructura general En general la estructura de un programa en lenguaje ensamblador tiene la siguiente forma:
Programación en ensamblador Los datos se definen en las secciones .data y .bss. Para definir datos en la sección .data se pueden utilizar las siguientes directivas:
Programación en ensamblador El formato para estas directivas es el siguiente: etiqueta: directiva valor Ejemplo: declaración de variables inicializadas Se pueden definir múltiples valores en la misma línea. Cada uno de ellos será guardado en memoria en el orden en el cual fueron declarados.
Programación en ensamblador Declaración de múltiples valores con una misma etiqueta En este caso cuando se lee la variable var arroja el valor 10, para poder leer el siguiente valor se debe incrementar la dirección de var en 4 bytes (debido a que la variable está declarada como long, es decir de 32 bits) de esta manera se usa la etiqueta var como la dirección inicial de estos valores y su tratamiento es el de un arreglo donde cada acceso se realiza tomando var como posición inicial lo cual sería equivalente a decir var[0] y las posiciones siguientes como un desplazamiento de 4 bytes cada uno. Para leer por ejemplo el valor 30 se accesaría var+8.
Programación en ensamblador Cuando se definen las variables, el sistema las guarda en forma consecutiva en memoria. Por ejemplo si se definen variables de 16 bits y luego se leen usando instrucciones de 32 bits el sistema no produce un mensaje de error y accesa los bytes consecutivos leyendo datos no válidos. Aún cuando la sección .data se utiliza principalmente para definir variables también puede ser usada para definir constantes. Esto se hace usando la directiva .equ, el formato para esta directiva es: directiva símbolo, valor Ejemplo: definición de constantes
Programación en ensamblador Para definir datos en la sección bss se usan dos directivas: La directiva .lcomm se usa para datos locales, que no serán usados fuera del código local. El formato, para ambas directivas es el siguiente: directiva símbolo, tamaño en bytes Ejemplo : declaración de un área de memoria sin inicializar Se declara una variable llamada area de 100 bytes de tamaño.
Programación en ensamblador La ventaja de declarar variables en la sección .bss es que esos datos no se incluyen en el programa ejecutable y por lo tanto el tamaño total del programa es menor al tamaño generado por la declaración equivalente en la sección .data.
Programación en ensamblador Instrucciones Las instrucciones en gas tienen un sufijo que indica el tamaño del dato sobre el cual actúa la instrucción. Las instrucciones pueden no tener operandos, tener un sólo operando o dos operandos; dependiendo de la instrucción en particular. En general las instrucciones tienen la forma: instrucción operando fuente, operando destino
Programación en ensamblador Los operandos se pueden clasificar en tres tipos: • Inmediato: para valores constantes. • Registro: denota el contenido de uno de los registros. • Referencia a memoria: denota el contenido de una posición de memoria.
Programación en ensamblador Hay varias maneras de obtener la información las cuales se pueden resumir en la siguiente tabla: inm denota un inmediato reg denota un registro, regb un registro base y regi un registro índice e es la escala la cual puede ser 1, 2, 4 ó 8 R[reg] significa el contenido del registro reg M[x] significa el contenido de la posición de memoria con dirección x
Programación en ensamblador Ejemplo: valores para cada modo de direccionamiento Asumiendo los contenidos de: %eax= 0x100 y %ecx= 0x10
Programación en ensamblador El valor inmediato se puede expresar en decimal o en hexadecimal como se puede observar en el siguiente ejemplo: Asumiendo el contenido de %eax=0x100
Programación en ensamblador La instrucción mov La instrucción mov permite el movimiento de datos, ya que gas utiliza un prefijo para señalar el tamaño de los datos podemos tener tres opciones al momento de realizar una transferencia de datos: movb mueve un byte movw mueve una palabra (2 bytes) movl mueve dos palabras (4bytes) Usaremos la nomenclatura F para denotar el operando fuente y D para denotar el operando destino.
Programación en ensamblador La instrucción mov La instrucción mov permite el movimiento de datos, ya que gas utiliza un prefijo para señalar el tamaño de los datos podemos tener tres opciones al momento de realizar una transferencia de datos: movb mueve un byte movw mueve una palabra (2 bytes) movl mueve dos palabras (4bytes) Usaremos la nomenclatura F para denotar el operando fuente y D para denotar el operando destino.
Programación en ensamblador Movimiento de datos inmediatos a registro o a memoria Los datos inmediatos se especifican directamente en la instrucción. Deben estar precedidos por el símbolo dólar para indicar que son datos inmediatos. Pueden estar expresados en decimal o hexadecimal. Ejemplo:
Programación en ensamblador Movimiento de datos entre registros Esta es la transferencia de datos que toma menor tiempo dentro del sistema es buena práctica de programación utilizar este tipo de transferencia en vez de accesos a memoria ya que ello redunda en una mayor eficiencia. Ejemplo:
Programación en ensamblador Movimiento de datos entre memoria y registros Las direcciones de memoria usualmente se expresan con etiquetas, cuando por ejemplo se escribe: a debe haber sido declarado en la sección de datos. Como se están transfiriendo 4 bytes éstos serán guardados en memoria de manera consecutiva a partir de la posición a.
Programación en ensamblador Movimiento de datos entre memoria y registros Para mover la información de memoria a un registro se escribe:
Programación en ensamblador Para leer o escribir en arreglos se utiliza direccionamiento indexado. Hay que tomar en cuenta la dirección inicial del arreglo y utilizar un índice para ir recorriendo cada uno de sus elementos. El tamaño de los datos representa el desplazamiento entre dos posiciones del arreglo. La forma general de accesar un arreglo se puede expresar como: dirección inicial (desplazamiento, índice, escala) La dirección se calcula como: dirección inicial + desplazamiento + índice *escala Donde escala refleja el tamaño del tipo de dato del arreglo.
Programación en ensamblador Ejemplo de declaración y lectura de un arreglo de enteros:
Programación en ensamblador Movimiento de datos con extensión Hay dos instrucciones adicionales que permiten mover datos extendiéndolos.
Programación en ensamblador Movimiento de datos con extensión Hay dos instrucciones adicionales que permiten mover datos extendiéndolos. La instrucción movsbl toma un operando fuente de 1 byte, ejecuta una extensión de signo a 32 bits y lo copia al destino de 32 bits. La instrucción movzbl hace un procedimiento similar pero extiende con ceros. Ejemplo de movsbl: Ejemplo de movzbl:
Programación en ensamblador Uso de la pila La pila es un área de memoria que crece desde una dirección inicial hacia direcciones menores. El último elemento colocado en la pila es el que está disponible para ser retirado. Las instrucciones para el manejo de la pila son dos, una para apilar un operando fuente y una para desapilar el valor que está en el tope de la pila y colocarlo en un operando destino.
Programación en ensamblador Uso de la pila Ejemplo de uso de las instrucciones pushl y popl: Dados los valores iniciales: %esp=0x108 %ebx=0xCD %eax=0xAB %ecx=0xFE Al ejecutarse las instrucciones: Ocurre lo siguiente:
Programación en ensamblador Uso de la pila Carga dirección efectiva (instrucción leal) La instrucción leal (load effective address) permite obtener la dirección de un operando en vez de su valor.
Programación en ensamblador Carga dirección efectiva (instrucción leal) La instrucción leal (load effective address) permite obtener la dirección de un operando en vez de su valor. Ejemplo: Dados los siguientes valores en los registros: %eax=0x100 %ebx=0x10
Programación en ensamblador Instrucciones aritméticas y lógicas Para estas instrucciones usaremos el sufijo l para los ejemplos aunque también se pueden usar con b o w.
Programación en ensamblador Instrucciones aritméticas y lógicas
Programación en ensamblador Operaciones aritméticas especiales