Ensamblador X86 y C

Josué Acevedo Maldonado
Nabucodonosor Editorial
4 min readMay 8, 2020

La interconexion de dos mundos, no tan ajenos entre si.

Photo by Jefferson Santos on Unsplash

En 1970 Ken Thompson, creador del lenguaje B, construyo la primera versión del sistema operativo UNIX en los laboratorios Bell empleando su lenguaje de programación. Sin embargo, B almacena los datos en memoria en el espacio de un byte, por lo que el procesar un dato como un número complejo o uno real es trabajo del programador, lo que hacía tedioso el desarrollo. Dennis Ritchie en 1972 en los laboratorios Bell, crea el lenguaje C a partir de mucha de las características existentes en B, por lo que es visto como una evolución del mismo, además comenzó a ganar popularidad debido a que el sistema operativo UNIX fue desarrollado en ese lenguaje.

En la actualidad la gran mayoría de los sistemas operativos están escritos en C/C++. Debido a la naturaleza del lenguaje C, ya que fue echo para ser usado en el desarrollo de software de sistemas (sistemas operativos y compiladores), su diseño permite combinarlo con código Ensamblador y también es posible invocar funciones de C desde Ensamblador, en esta publicación se abordará como realizar ambas cosas.

Ensamblador en C

La inclusión del lenguaje ensamblador en C fue necesario debido a que, al construir un sistema operativo se requiere tener acceso total al hardware, por lo que se construyen funciones que permitan leer los caracteres introducidos por teclado, imprimir caracteres en pantalla, manejar las interrupciones, etc.

Se emplea el ensamblador para realizar esas operaciones y utilizarlas después a lo largo del desarrollo del sistema operativo como llamadas a funciones, lo que resulta más cómodo y eficiente.

Suma de dos números en C con Ensamblador.

#include <stdio.h>
#include <stdint.h>
int suma(int dato, int dato2){
int salida; // en C un dato de tipo int ocupa 32 bits (4 bytes)

asm volatile ( // esta instrucción permite colocar código
// ensamblador en el programa c
"addl %%ebx, %%eax;" // codigo ensamblador Gnu Assembler,
// soportado por el compilador gcc existente en GNU/Linux
: "=a" (salida) // la letra 'a' hace referencia al operando
// destino que en la sintaxis at&t, refiere al registro eax
// el símbolo = indica que se copiara el valor del registro
// eax a una variable, en este caso 'salida', una vez se
// haya ejecutado la instrucción
: "a" (dato), "b" (dato2) // esta instrucción indica que se
// copiara el contenido de la variable dato al operando destino
// (eax) y el contenido de la variable dato2 se copiara al
); // operando fuente (ebx)
return salida;
}
int main(){ int resultado = 0;
resultado = suma(5,2); // uso de la función suma
printf("resultado = %d\n", resultado); // impresión en pantalla
// de la variable 'resultado'
return 0;
}

Para generar un archivo ejecutable se emplea el compilador GCC, al cual se le especificara el nombre del ejecutable y el nombre del archivo.

/code # gcc -o ensamblador_c ensamblador_c.c
/code # ./ensamblador_c
resultado = 7
/code #

C en Ensamblador

También es posible utilizar las librerías de C desde ensamblador, esto es útil cuando se desconocen las interrupciones de software del sistema operativo y los valores a colocar en los registros para utilizar los servicios, de esta manera es el compilador de C quien se encarga de resolverlos.

Uso de la función printf de C en Ensamblador (GNU/Linux).

extern printf ; invocacion de la funcion printf de Csection .data
mensaje db 'Hello',10,13, 0 ; en C se utiliza el byte cero
; (carácter nulo) para indicar el final de una cadena
section .text
global main ; la etiqueta main se indica como global para
; que el ligador lo encuentre y ensamble
main: ; inicio del procedimiento main (funcion main de c)
push mensaje ; inserción en la pila de la cadena a
; imprimir
call printf ; invocacion de la funcion printf de c
add esp, 4 ; eliminación de los últimos 4 bytes de
; la pila donde la función printf almaceno ciertos datos
ret ; retorno del procedimiento main

Ya que el código anterior utiliza el formato de llamada a función de 32 bits, se debe de ensamblar a 32 bits y al enlazar emplear el compilador GCC con la bandera -m32, de lo contrario se realizará el enlazado a 64 bits lo que lanzaría un error, una vez hecho esto se obtendrá el archivo ejecutable.

/code # nasm -f elf32 c_ensamblador.asm
/code # gcc -m32 c_ensamblador.o -o c_ensamblador
/code # ./c_ensamblador
Hello
/code #

Josue Acevedo Maldonado es ingeniero de software, trabaja actualmente como consultor.

Conectarse en LinkedIn.

¡Gracias por ser parte de la comunidad!
Puede encontrar contenido relacionado en el canal de YouTube, Twitter, Twitch, Spotify, etc, ademas del libro Ensamblador X86.

Si ha disfrutado de este artículo y siente que ha aprendido algo valioso, por favor compártalo.

¡Gracias por leer!

--

--

Josué Acevedo Maldonado
Nabucodonosor Editorial

Amante de la tecnologia y con pasion en resolver problemas interesantes, consultor, y creador del canal de youtube NEOMATRIX. https://linktr.ee/neomatrix