• [Tutorial - C/C++ + NASM] Paso de funciónes y stack frames

 #436132  por NvK
 07 Feb 2014, 00:44
Introducción:
Hoy vengo con un tema sumamente importante para aumentar el poder de C/C++.
Y me decidí de hacer una guia paso a paso, ya que en internet no pude encontrar mucho al respecto
y si bien se documenta el uso de nasm es difícil encontrar algo más "detallado", con ésto espero
ayudar a la gente que recién comienza con ambos lenguajes.

Nuestro objetivo:
Programar nuestra primera función en Nasm y llevarla a C/C++ entendiendo como funcióna el stack frame.

¿Por que Nasm?
Netwide Assembler es sin duda, el assembler más potente y portable(ademas de poder generar varios formatos binarios).
Con el no tienes límites, puedes programar ROMS, boot loaders, sistemas operativos, etc...
Lo más fácil es su uso, con la sintaxis de Intel.
NOTA: ten en cuenta ésto ya que si en el algún momento te interesa programar éste tipo de cosas podría servirte.

Descargando Nasm:
Primero irse al sitio oficial, y descargarnos la version para windows:
[ Debe registrarse para ver este enlace ]
o
[ Debe registrarse para ver este enlace ]
y nos bajamos el nasm-2.11-win32.zip.

Preparando el entorno:
Ahora crearemos una carpeta del mismo nombre (nasm-2.11) y un archivo de nombre cualquiera .asm
en éste ejemplo el mio se llama n.asm

Stack frames:
Antes de escribir nuestra función tenemos que entender que es el stack frame y como usarlo a nuestro favor.
Los stack frames son muy usados en lenguajes de alto nivel para controlar el paso de parametros, normalmente
al comienzo de nuestra función se "configura" la pila, guardando éstos valores y de ésa manera ya disponemos
de un direccionamiento.
Ejemplo de configuración de pila en Intel x86:
push ebp ; guardo ebp
mov ebp, esp ; copio el puntero del stack a ebp
; a este punto ya disponemos de un direccionamiento.

... ; codigo

pop ebp ; restauramos ebp
ret ; salimos de la función
Recuerda que ésta es una de las muchas maneras que hay, sin embargo es necesaria entenderla para poder avanzar,
ya que sin comprender lo aspectos basicos se complicara más adelante.

Para "recoger" los parametros de la función tenemos que calcular la longitud de los registros, ésto es MUY facil...
"D" Representa su valor en decimal.
0D -> EBP : valor original.
4D -> Dirección de retorno.
Aca comienzan nuestros paramentros
8D -> Primer parametro.
12D -> Segundo parametro.
16D -> Tercer parametro.
...
En realidad partimos de ebp y nos desplazamos de 4 bytes en 4 bytes
por lo tanto podemos deducir que 4b + 4b = 8b.
Ejemplo:
push ebp ; configuro la pila
mov ebp, esp

mov ecx, [ebp+8] ; mover a ecx nuestro primer parametro.
mov eax, [ebp+12] ; mover a eax nuestro segundo parametro.

...

pop ebp ; restauramos
ret
Esquema:


Con éste código ya podemos empezar a escribir nuestra función pasando 2 parametros.

NOTA: También hay otros metodos de direccionamiento, como por ejemplo sustituir el "push ebp, etc.."
por ENTER y LEAVE.

En muchas ocasiones puede que veas algo parecido a un parametro pero con un "-" Ej:
sub esp, 8 ; reservamos espacio
mov ecx, [ebp-8]
Ésto copiara la variable local de ebp a ecx.

Escribiendo nuestra función:
No entrare en profundidad acerca de las secciones, ni para que sirve detalladamente cada registro
por que se supone que éste tutorial trata asm en efectos practicos...

Recuerda que en asm(y en general) los comentarios son representados por ";"
n.asm
[bits 32] ; tipo de arquitectura

global _mul_2_num

section .text ; sección de código
	_mul_2_num: ; el nombre de nuestra función
		push ebp ; configuración de la pila
		mov ebp, esp
		
		mov ecx, [ebp+8] ; primer param.
		mov eax, [ebp+12] ; segundo param.
		mul ecx ; esto multiplica ecx por el valor de eax, así funciona mul.
		
		pop ebp
	ret ; retorno de la función
El "_" es necesario cada vez que definas una función(por lo menos en win) para que pueda ser exportado junto
con global, ésto debe ser así.

Generando nuestro .obj:
Necesitamos el .obj para exportar nuestra función a C, para generar éste formato es tan facíl como usar el
siguiente comando:

nasm -f win32 n.asm

n obviamente es el nombre de nuestro archivo con su respectiva extensión.


Pasando nuestra función a C:
Ésta es la parte más fácil, crearemos un proyecto en C, yo uso codeblocks pero puedes llevar ésto a cualquier IDE.
Moveremos ese .obj a la carpeta en donde se cuentra nuestro proyecto, y linkearemos con la siguiente opcion.
n.obj (o cual sea el nombre de tu .obj)


código en C, por si no ves la imagen:
#include <stdio.h>
#include <stdlib.h>

extern mul_2_num(int *, int *); // exportando nuestra función.

int main()
{
    printf("Resultado: %d \n", mul_2_num(2, 3));

    return 0;
}

Si compilamos ésto el resultado sera por obviedad 6, y listo ya tenemos nuestra función optimizada de nasm a C.
Es cierto que no hable acerca de las convenciones de llamados como stdcall o cdecl(dentro de otras cosas),
pero creo que con ésto cubrimos el tema más importante del uso de nasm y C.
Aca te dejo un articulo que podría servirte de mucho: [ Debe registrarse para ver este enlace ]

Esto es todo por hoy, espero haberlos ayudado y recuerden comentar sus dudas si es que las hay.
Saludos NvK.
 #436722  por sanko
 19 Feb 2014, 19:40
No sabia de esta preciosidad xd, si habia leido que podía implementar la E/S de C en NASM pero no tenia ni idea de que se podía hacer esto.
Realmente me va a servir de mucho, un saludo maquina