LinuxParty
Es el momento para cambiar al fabuloso GNU as. Vamos a olvidarnos de DEBUG durante algún tiempo. Gracias depuración. GNU as, Gas, o GNU Assembler, es obviamente el ensamblador utilizado por el Proyecto GNU. Es parte del paquete Binutils, y actúa como el defecto de fondo de gcc. Gas es muy potente y puede apuntar a varias arquitecturas informáticas. Todo un programa, entonces.
Como la mayoría de los ensambladores, Gas 'se compone de directivas (también conocido como Pseudo Ops), comentarios, y por supuesto, las instrucciones. Las instrucciones son muy dependientes de la arquitectura de la computadora de destino. A la inversa, las directivas que tienden a ser relativamente homogénea.
1 Sintaxis
Originalmente, este ensamblador sólo aceptaba la sintaxis del ensamblador AT&T, incluso para las arquitecturas Intel x86 y x86-64. La sintaxis de AT&T es diferente a la incluida en la mayoría de las referencias de Intel. Hay varias diferencias, la más memorable es que las instrucciones tienen dos operandos que tienen el origen y los destinos en el orden inverso. Por ejemplo, la instrucción mov ax, bx
se expresaría en AT&T sintaxis como movw %bx,%ax
, es decir, el operando de la derecha es el destino y el de más a la izquierda es la fuente. Otra distinción es que los nombres de registro utilizados como operandos deben ser precedidos por el signo del porcentaje (%). Sin embargo, desde la versión 2.10, Gas admite la sintaxis de Intel por medio de la directiva intel_syntax.. Pero en el siguiente ejemplo vamos a utilizar la sintaxis AT&T.
2 Nuestras Metas
Ahora voy a utilizar la función de C printf. En C, el programa de saludo sería
int main()
{
printf("hello, world!\n");
return 0;
}
Hemos omitido la inclusión de la cabecera stdio.h. Podríamos recurrir a una sola frase: return printf("hello, world!\n") - 14;
pero creo que al utilizar dos frases que vamos a obtener un código más claro. Guardamos nuestro programa en un archivo llamado "hello.c", y compilamos como
gcc -o hello.exe hello.c
Si estamos trabajando en Windows, con el port MinGW de la colección de compiladores de GNU. Me gusta MinGW mucho, especialmente su capacidad para proporcionar funcionalidad nativa a través de llamadas a la API de Windows directamente, lo cual es bueno para el rendimiento de nuestras aplicaciones. Trabajando en Windows significa que nuestros ficheros ejecutables (código objeto y DLLs también) siguen el formato PE / COFF. El formato de archivo Portable Ejecutable (PE) es un contenedor de toda la información que requiere el cargador de Windows para ejecutar el código. PE es una versión modificada del formato de archivo de Unix COFF (de ahí la referencia PE / COFF.) Otro popular formato de archivo para el código ejecutable es ELF (Executable and Linkable Format), que es utilizado por Linux, la Nintendo Wii y DS, y la PlayStation 3. Por el momento, sólo tenemos que saber que el comportamiento de GNU as varía en función del formato de archivo de destino (en nuestro caso, el PE / COFF).
gcc también nos puede proporcionar el archivo de ensamblador x86 utilizado. He escrito gcc -S hello.c y este fue el resultado que obtuve:
.file "hello.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "hello, world!\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
call __alloca
call ___main
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
ret
.def _printf; .scl 2; .type 32; .endef
3 Explicaciones Código
Desde un punto de vista general, podemos identificar tres elementos en el listado superior. En primer lugar, tenemos directivas, que son símbolos que comienzan con un '.' (punto.) Como se ha dicho, las directivas son generalmente válidas para cualquier ordenador. Si el símbolo se inicia con una letra se tratará de instrucción de lenguaje ensamblador, es decir, que se convertirá en una instrucción en lenguaje de máquina, y seguramente serán diferentes entre las arquitecturas informáticas. Por último, las etiquetas son los símbolos inmediatamente seguido por un ':' (colon o dos puntos.) Podemos pensar en las etiquetas como "direcciones" de los datos o código. Ahora vamos a hacer una revisión superficial de unas pocas directivas, así que ten paciencia.
.file string
Esta directiva identifica el inicio del archivo lógico (y la cadena debe ser el nombre del archivo.) En realidad, la directiva se ignora y sólo está allí para la compatibilidad con versiones anteriores. Podemos quitarlo.
.def name … .endef
Este par de directivas adjuntan información de depuración para el nombre del símbolo, y sólo se observan cuando Gas está configurado para la salida de formato PE / COFF. Pero nosotros no lo necesitamos para un programa tan simple como Hello World.
.section name
Esta directiva indica que el código se tiene que montar en una sección llamada nombre. Para los objetivos PE / COFF, la directiva .section
es usada en una de las maneras siguientes:
.section name [, "flags"]
.section name [, subsegment]
La salida de gcc recurre a la forma de flags, y, específicamente, dos flags (carácter) se utilizan para indicar los atributos de la sección: d (sección de datos) y r (. Sección de sólo lectura) Pero de nuevo, no es necesario para señalar explícitamente atributos de sección en nuestro simple programa.
.ascii “string”
Define uno o más literales de cadena (separados por comas). Cada cadena se monta en direcciones consecutivas (sin carácter nulo al final.)
.text subsection
Indica a Gas ensamblar las siguientes sentencias sobre el extremo de la subsección texto numerado subsection. Si se omite subsection (como es nuestro caso), el número que utiliza subsection es cero. Claramente, esta directiva es obligatoria, o gas no reunirá el código para imprimir nuestro mensaje "Hello World"
.global symbol (or .globl symbol)
.global
hace symbol visible al enlazador. En nuestro caso, queremos informar al enlazador sobre la función _main
que se esperaba. Para la compatibilidad con otros ensambladores, ambas grafías ( .global
o .globl
) son válidas.
Ahora, las directivas están hecha. Después ded la etiqueta _main
sólo tenemos el código ensamblador hasta la instrucción ret
. Parte de este código debería quedar claro si tiene experiencia previa en programación en ensamblador. Sin embargo, vamos a revisar estas instrucciones. Tenga en cuenta que la "l" al final de cada mnemónico le indica a Gas que se quiere utilizar la versión de la instrucción que trabaja con operandos "largos" (32-bits).
Las Primeras 3 instrucciones son el código típico para la inicialización de la pila:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
Al restar 8 bytes de ESP estamos reservando el espacio en la pila para contener variables locales (la pila Intel "crece" desde posiciones de memoria alta a las inferiores.) Luego tenemos el raro
andl $-16, %esp
Recuerde que en hexadecimal, -16 se expresa como 0xfffffff0. Por lo tanto, esta and
alinea la pila con la siguiente más baja 16-byte de dirección. Las razones de esta alineación no están muy claras para mí. Puede ser una elección de gcc con el fin de acelerar los accesos de punto flotante, o puede ser para la compatibilidad con una arquitectura particular. Cualquiera de ellos, no requieren tal alineación para mostrar hola, mundo!
El código siguiente es principalmente una manera muy artificial de almacenar un valor en EAX:
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
Es evidente que el código no está optimizado, ya que hay un montón de líneas innecesarias. Por otra parte, el valor final de EAX también se almacena en la memoria antes reservado a la pila. Parece que el valor en EAX es un parámetro para la invocación _alloca
en las dos líneas siguientes:
call __alloca
call ___main
Estos dos llamadas son innecesarias para nuestra aplicación. No vamos a ahondar en detalles, pero voy a decir la alloca()
es una función que se utiliza para asignar memoria en la pila. Y si los binarios PE / COFF se utilizan, y nuestra aplicación tiene una función int main()
, una función void __main()
debe ser llamada lo primero después de entrar en main()
. Vamos a dejarlo así por ahora. Más información se puede encontrar en este excelente artículo de OSDevWiki e instructivo .
Por último, nos encontramos con el código útil
movl $LC0, (%esp)
call _printf
Se mueve la dirección de la cadena ascii en la pila, e invoca printf
. Ahora, ¿dónde está la definición de printf
? Bueno, vamos a tomar de la biblioteca de C, por supuesto. El enlazador (ld) es responsable de la asociación de nuestro código con la definición de printf
.
Por último, encontramos
movl $0, %eax
leave
ret
Estas instrucciones constituyen el "código de retorno." Almacenar el valor de retorno (0 == éxito!) En EAX, destruir la pila, y el puntero de instrucción pop salvada la pila con el fin de devolver el control al procedimiento de llamada o programa.
Si le quitamos todas las líneas innecesarias, nuestra Hola, mundo! adquiriría este formulario:
.data
LC0:
.ascii "hello, world!\n\0"
.text
.global _main
_main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
ret
Más corto y más claro. He assambled, paso a paso:
as -o hello.o hello.s ld -o hello.exe /mingw/lib/crt2.o C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/crtbegin.o -LC:/MinGW/bin/../lib/gcc/mingw32/3.4.5 -LC:/MinGW/lib hello.o -lmingw32 -lgcc -lmsvcrt -lkernel32 C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/crtend.o
Pero se, que es más rápido escribir:
gcc -o hello.exe hello.s
-
Programación
- Gracias a la IA, el nuevo lenguaje de programación más popular es...
- Cómo instalar y utilizar Scikit-Learn en Linux
- Thomas E. Kurtz, coinventor de BASIC, muere a los 96 años
- Profesor de informática del MIT prueba el impacto de la IA en la formación de programadores
- Lanzamiento del IDE de código abierto Qt Creator 14 con soporte para complementos basados en Lua
- Plantillas para Joomla - Episodio 1: Plantillas, marcos y clubes o no...
- Este es el mejor libro que he visto para aprender a programar en Python en castellano desde cero, gratis y online
- ¿Deberían los niños seguir aprendiendo a programar en la era de la IA?
- La 'obsolescencia' de VBScript confirmada por Microsoft y su eventual eliminación de Windows
- El Gran Debate: ¿Deberían los Modelos de Inteligencia Artificial Ser de Código Abierto?
- El lenguaje de programación BASIC cumple 60 años
- El CEO de Nvidia dice que los niños no deberían aprender a programar
- 40 años de Turbo Pascal: recuerdos del dinosaurio codificador que revolucionó los IDE
- Los lenguajes de programación más populares y dónde aprenderlos.
- Top 5 de los principales lenguajes de programación para desarrollar aplicaciones de escritorio Linux