Alguna vez se preguntaron por que un programa como este puede llegar a ocupar unos 24KB?
int main(int argc, char *argv[],char *env[])
{
return 50;
}
Este programa no hace NADA, solo nos devuelve un 50 (si recuerdan nuestro último tutorial esto es mov eax,50). El 'problema' es que VC++ por defecto incorpora su Run Time Library que contiene muchisimas funciones indispensables como strlen,malloc,printf y muchas más. Esta Run Time Library ocupa bastantes KB, si hacemos un programa extenso su tamaño no tiene la más mínima importancia, pero si sólo escribimos un par de instrucciones ...
Linkeen el código anterior con esta linea de comandos:
link codigo.obj /NODEFAULTLIB /ENTRY:mainPaso a explicar, NODEFAULTLIB le indica al linker que quite las librerias que utiliza para resolver referencias externas por defecto.
Entry le indica al linker la función que dará comienzo al programa que por defecto nunca es main, nuestras funciones main o WinMain son llamadas por otras que antes hacen inicializacion de la Heap para utilizar malloc y free, que nos devuelven argc, argv y env por ejemplo. Estas funciones se llaman mainCRTStartup para main y WinMainCRTStartup para WinMain.
Que hemos logrado con esta linea de comandos? Reducir el tamaño de nuestro ejecutable a nada más y nada menos que 1024 bytes (si no lo consiguieron es porque tienen una versión del linker que usa un align grande prueben con:
link codigo.obj /NODEFAULTLIB /ENTRY:main /ALIGN:512 y lo van a conseguir).
Esto está muy bien pero en realidad no nos sirve de mucho, ahora tenemos un programa que está discapacitado, no puede llamar ni siquiera a la función strlen(). Pero como el título de este tutorial dice vamos a crear nuestra propia Run Time Library para reemplazar la de VC++, el objetivo no es hacer una mejor que los muchachos de Microsoft, sino tener nuestra propia Run Time Library unicamente con las funciones que necesitemos y de paso aprender MUCHO sobre programación.
Primero vamos a ver un ejemplo de Run Time Library muy simple escrito en C++. Nuestro objetivo en este momento es compilar este programa (COMUN.CPP) sin la STDLIB por defecto:
#include <windows.h>
int main(int argc, char *argv[],char *env[])
{
char *dos = (char*)malloc(55);
int x = strlen("hola");
wsprintf(dos,"%d",x);
MessageBox(0,dos,dos,0);
free(dos);
return 50;
}
Con esta linea de comandos
LINK COMUN.OBJ /NODEFAULTLIB user32.lib kernel32.lib nos enteramos que nos faltan un par de funciones, estas cuatro exactamente:
COMUN.OBJ : error LNK2001: unresolved external symbol _free
COMUN.OBJ : error LNK2001: unresolved external symbol _strlen
COMUN.OBJ : error LNK2001: unresolved external symbol _malloc
LINKASM : error LNK2001: unresolved external symbol _mainCRTStartup
COMUN.exe : fatal error LNK1120: 4 unresolved externals
Al crear nuestra libreria la vamos a hacer lo más modular posible, porque cada archivo objeto (código compilado) que sea llamado se cargará completamente, por ejemplo si tenemos todas las funciones dentro de un solo archivo .cpp, lo compilamos y lo solo utilizamos strlen() dentro del programa que usa nuestra Run Time Library también serán cargadas strcpy(),malloc(), free() y todas las funciones que hagamos.
Sabiendo esto que es muy importante vamos a crear cuatro archivos, C_STR.CPP con las funciones strlen() y strcpy(), C_MAIN.CPP con mainCRTStartup, C_WMAIN.CPP con WinMainCRTStartup y C_ALLOC.CPP con malloc() y free().
C_STR.CPP:
extern "C" size_t strlen(const char *s)
{
unsigned long Result = 0;
while(*s++) { Result++; }
return Result;
}
extern "C" char *strcpy(char *dest, const char *src)
{
char *cp = dest;
while(*cp++ = *src++);
return(dest);
}
C_MAIN.CPP:
extern int main(int argc, char *argv[],char *env[]);
extern "C" void mainCRTStartup() { main(0,0,0); }
C_WMAIN.CPP:
#include <windows.h>
extern WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow);
extern "C" void WinMainCRTStartup() { WinMain(GetModuleHandle(NULL),NULL,GetCommandLine(),SW_SHOW); }
C_ALLOC.CPP:
#include <windows.h>
void *malloc(size_t size) { return HeapAlloc(GetProcessHeap(),NULL,size); }
void free(void *block) { HeapFree(GetProcessHeap(),NULL,block); }
Bueno, la mayoria de las cosas las deben entender sin ningún problema, C_STR.CPP tiene dos funciones muy simples strlen() y strcpy() de la STDLIB. C_MAIN.CPP tiene una función que llama al main del programa que estemos escribiendo y le pasa como parametros el argc, argv y env (en este caso solo le pasamos ceros para no complicarnos). C_WMAIN.CPP llama a WinMain() la instancia anterior del programa, la linea de comandos y SW_SHOW. Finalmente C_ALLOC.CPP usa la Heap del proceso para asignar y liberar memoria.
Ahora que tenemos todos los archivos de la CLIB (nuestra libreria para reemplazar la Run Time Library de VC++) podemos compilarla y linkearla asi:
cl /c C_ALLOC.CPP C_MAIN.CPP C_STR.CPP C_WMAIN.CPP
lib /out:CLIB.LIB C_ALLOC.OBJ C_MAIN.OBJ C_STR.OBJ C_WMAIN.OBJEsto nos va a generar nuestra clib.lib.
Ahora teniendo el comun.cpp podemos hacer:
LINK COMUN.OBJ /NODEFAULTLIB user32.lib kernel32.lib clib.lib y se va a generar un ejecutable que nos va mostrar un MessageBox con el numero 10. Ocupa 2.560 bytes en mi PC. Si prueban a compilar el mismo programa con la STDLIB por defecto verán que lo mínimo que consigan serán 14.484 bytes.
En la segunda parte vamos a ver como mejorar nuestra libreria con más rutinas, como por ejemplo implementar los operadores new y delete, entre otras cosas.
Saludos,
Mariano.
Autor: Mariano Ventaja
http://www.c0d3rz.com.arDescarga:
http://c0d3rz.com.ar/foro/viewtopic.php?t=85