2
Nov
2012

Punteros a funciones: desde C a Assembler

En C tenemos la posibilidad de pasar punteros a funciones como parámetros de una función. Esto permite realizar llamadas a la función-parámetro desde la función. Si por ejemplo lo combinamos con parámetros de tipo *void (para la función-parámetro), podría llegar a verse como una forma rústica de polimorfismo.

Ejemplo:

#include <stdlib .h>
#include <stdio .h>
 
int foo(int);
void bar(int (*)(int));
 
int main(int argc, char *argv[]){
 
	bar(foo);
 
	return 0;
}
 
int foo(int num){
	return num;
}
 
void bar(int (*func)(int)){
	int res = (*func)(3);
	printf("res = %d\n", res);
}
</stdio></stdlib>


Leer el resto del artículo »

27
Oct
2012

Linux: analizando el stack de un proceso

En este breve artículo quiero presentar un análisis básico del stack de un proceso en Linux. Se trata de un caso de ejecución sencillo, contado como un storyboard, para ir a los conceptos fundamentales del código ejecutable (visto como assembler), llamadas a funciones y manejo de memoria. La arquitectura en este caso es IA-32.

1. Tenemos un programa sencillo escrito en C

#include <stdio .h>
#include <stdlib .h>
 
int main(void){
 
    int x;
    int y;
 
    x = 4;
    y = 5;
 
    foo();
 
    return 0;
}
 
int foo(){
    int y = 1;
    return y;
}
</stdlib></stdio>

Se declaran dos variables locales en la función main, se llama a una función foo y se declara una variable local en la función foo.


Leer el resto del artículo »

9
Sep
2012

Desde un High-Level Language a un archivo ejecutable

pasaje de high level language a archivo ejecutable assembler

Professional Assembly Language – Richard Blum (2005)

La figura describe el proceso que se desarrolla para traducir código escrito en un lenguaje de alto nivel (High-Level Language) a código de máquina (contenido en un archivo binario ejecutable).

El compilador cumple la función de transformar el código escrito en el lenguaje de alto nivel a código objeto. El código objeto es código de máquina que aún no se encuentra en condiciones de ser ejecutado, por las razones que veremos más adelante. Este código depende de la arquitectura de procesador para la cuál se compile (por ejemplo IA-32, ARM, etc.).

En lenguajes como C y C++, se suele generar un archivo de código objeto por cada archivo con código de alto nivel -contando los headers y su implementación en forma conjunta-.

La mayoría de los programas se componen de múltiples archivos con código de alto nivel. Existe una relación entre estos archivos para definir estructuras complejas, reutilización de componentes, separación de funcionalidades e interacciones.


Leer el resto del artículo »

30
Aug
2012

Función de Prólogo y Epílogo (lenguaje assembler)

Las funciones de Prólogo y el Epílogo son una convención (vigente para muchos lenguajes de alto nivel, como por ejemplo C) que los compiladores aplican al generar el código assembler correspondiente a una función. El objetivo es establecer una protección contra los buffer overflows.

Supongamos el siguiente código en C:

int obtener_numero_fijo();
 
int main(void) {
	return 1;
}
 
int obtener_numero_fijo(){
	return 2;
}

Luego de compilarlo (con GCC), analizamos el código objeto (con objdump) y observamos el siguiente código assembler:

00000000 
: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: b8 01 00 00 00 mov $0x1,%eax 8: 5d pop %ebp 9: c3 ret 0000000a : a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: b8 02 00 00 00 mov $0x2,%eax 12: 5d pop %ebp 13: c3 ret

Podemos observar que ambas funciones comienzan poniendo el valor del registro ebp en el stack. Luego mueven el stack pointer al registro ebp. Al finalizar la función, se retira el último valor del stack y se guarda en el registro ebp.


Leer el resto del artículo »