Miércoles 20 de Noviembre de 2024, 14:22
SoloCodigo
Bienvenido(a),
Visitante
. Por favor,
ingresa
o
regístrate
.
¿Perdiste tu
email de activación?
Inicio
Foros
Chat
Ayuda
Buscar
Ingresar
Registrarse
SoloCodigo
»
Foros
»
Programación General
»
C/C++
(Moderador:
Eternal Idol
) »
[Artículo]
Crear nuestra propia Run Time Library (Tercera Parte)
« anterior
próximo »
Imprimir
Páginas: [
1
]
Autor
Tema:
[Artículo]
Crear nuestra propia Run Time Library (Tercera Parte) (Leído 3938 veces)
Eternal Idol
Moderador
Mensajes: 4696
Nacionalidad:
[Artículo]
Crear nuestra propia Run Time Library (Tercera Parte)
«
en:
Jueves 1 de Julio de 2004, 10:58 »
0
Crear nuestra propia Run Time Library (Tercera Parte)
En la MSDN encontré algo interesante sobre la opción /ENTRY del linker:
"Type a function name in the Entry-Point Symbol text box (or in the function argument on the command line). The function must be defined with the __stdcall calling convention. The parameters and return value must be defined as documented in the Win32 API for WinMain (for an .EXE file) or DllEntryPoint (for a DLL). It is recommended that you let the linker set the entry point so that the C run-time library is initialized correctly, and C++ constructors for static objects are executed."
Vamos a probar un poco de código con objetos estáticos:
ESTATICOS.CPP
Código: C++
#include <windows.h>
void
SayLong
(
int
number
)
{
char
Data
[
10
]
;
ltoa
(
number,Data,
10
)
;
MessageBox
(
0
,Data,
"El Numero"
,
0
)
;
}
class
Man
{
public
:
Man
(
)
{
SayLong
(
66
)
;
}
~Man
(
)
{
SayLong
(
99
)
;
}
}
;
Man Jacinto
;
Man Pedrito
;
void
main
(
)
{
}
Compilando y linkeando este código con la Run Time Library por defecto conseguimos cuatro MessageBox, dos con 66 (el constructor) y dos con 99 (el destructor).
Ahora tratamos de compilar y linkear con nuestra Run Time Library y nos encontramos con lo siguiente:
estaticos.obj : error LNK2001: unresolved external symbol _atexit
estaticos.exe : fatal error LNK1120: 1 unresolved externals
Si vamos a la MSDN podemos ver que lo que hace esta función es ejecutar la función que le es pasada como parametro al final del programa. Usando una estructura de pila (LIFO).
Entonces que nos detiene? Que nos imposibilita crear una función simple como esa? Nada, manos a la obra señores.
C_ATEXIT.CPP
Código: C++
#include <malloc.h>
unsigned
long
*
Funcs
=
0
;
int
n_funcs
=
0
;
extern
"C"
int
atexit
(
void
(
__cdecl
*
func
)
(
void
)
)
{
if
(
!
Funcs
)
{
Funcs
=
(
unsigned
long
*
)
malloc
(
1
)
;
}
Funcs
=
(
unsigned
long
*
)
realloc
(
Funcs,
(
n_funcs
+
1
)
*
4
)
;
Funcs
[
n_funcs
]
=
(
unsigned
long
)
func
;
n_funcs
++
;
return
0
;
}
extern
"C"
void
destructores
(
)
{
for
(
int
x
=
n_funcs
-
1
;
x
>=
0
;
x
--
)
{
void
(
__cdecl
*
calla
)
(
void
)
=
(
void
(
__cdecl
*
)
(
void
)
)
Funcs
[
x
]
;
calla
(
)
;
}
free
(
Funcs
)
;
}
Hermoso el código verdad? Cada vez que se llama a la función atexit() se comprueba si ya tiene valor la estructura donde vamos a guardar los punteros a las funciones. Se realloca la memoria necesaria y se le asigna al elemento actual el parametro func. Siempre devolvemos cero que indica que no hubo ningún error.
Pero este código hermoso que hace exactamente hasta ahora? Ir llenando una lista de punteros a funciones, perfecto pero nos falta algo muy importante, recordemos que atexit() crea una lista de funciones que van a ser llamadas al final del programa. El principio del programa es la función de ENTRADA (solo nos interesan main, WinMain y DllMain ya que si fuera otra no usaría la Run Time Library, ni la nuestra ni la de VC++) que sabemos como se ejecuta, la llamamos nosotros desde nuestra Run Time Library por lo tanto sabemos perfectamente cuando termina de ejecutarse; en la siguiente instrucción a la llamada a main() el programa debe ejecutar la lista de funciones de atexit() y limpiar el buffer utilizado. Estas dos cosas las hace la función destructores() que está también en C_ATEXIT.CPP.
Ahora mismo podríamos compilar el programa ESTATICOS.CPP pero no pasaría nada, la compilación y el linkeo serían satisfactorios pero nuestro programa no haría absolutamente nada.
La pregunta del millón, quien llama a la función atexit(): la propia Run Time Library la llama. Acertaron? No me mientan eh.
Y cuando y porque la llama? Porque el compilador le pasa el puntero del destructor al ejecutar el constructor, por lo tanto, al no tener implementado nuestro código para manejar los constructores estáticos no recibimos ninguna llamada a atexit() y destructores() se ejecuta sin ningún puntero al que llamar.
Que hace el compilador para decirnos que existen constructores de objetos estáticos? El compilador crea un función diferente para cada uno de los objetos estáticos (esto lo podemos comprobar viendo las direcciones que le pasa por cada objeto a atexit) y nos pasa dentro del ejecutable las 'etiquetas' (posición en el archivo) de esas funciones a ejecutarse.
data_seg
#pragma data_seg( ["section-name"[, "section-class"] ] )
Specifies the default section for data. For example:
#pragma data_seg( "MY_DATA" )
causes data allocated following the #pragma statement to be placed in a section called MY_DATA.
Data allocated using the data_seg pragma does not retain any information about its location.
Estas secciones que se definen con #pragma data_seg son escritas por el linker dentro del ejecutable y tienen punteros, a inicializadores de C, constructores de C++, pre-terminadores de C y terminadores de C que forman tablas, estas se guardan en variables que tienen el comienzo y el fin de la tabla y son pasadas a una función llamada _initterm() que se encarga de llamar a todos esos punteros ejecutando su código. Esta es la forma en la que se llama a al constructor de de un objeto estático.
Nuestro nuevo C_MAIN.CPP:
Código: C++
#include <malloc.h>
#include <string.h>
extern
"C"
int
__argc
=
1
;
extern
"C"
char
**
__argv
=
0
;
extern
"C"
void
__stdcall ExitProcess
(
unsigned
long
uExitCode
)
;
extern
"C"
char
*
__stdcall GetCommandLineA
(
void
)
;
extern
int
main
(
int
argc,
char
*
argv
[
]
,
char
*
env
[
]
)
;
extern
"C"
int
__stdcall MessageBoxA
(
int
a,
char
*
b,
char
*
c,
unsigned
int
p
)
;
typedef
void
(
__cdecl
*
_PVFV
)
(
void
)
;
#pragma data_seg(".CRT$XIA")
_PVFV __xi_a
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XIZ")
_PVFV __xi_z
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XCA")
_PVFV __xc_a
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XCZ")
_PVFV __xc_z
[
]
=
{
0
}
;
typedef
void
(
*
PFV
)
(
void
)
;
#pragma data_seg(".CRT$XPA")
PFV __xp_a
=
0
;
/* C pre-terminators */
#pragma data_seg(".CRT$XPZ")
PFV __xp_z
=
0
;
#pragma data_seg(".CRT$XTA")
PFV __xt_a
=
0
;
/* C terminators */
#pragma data_seg(".CRT$XTZ")
PFV __xt_z
=
0
;
extern
"C"
void
__cdecl _initterm
(
_PVFV
*
, _PVFV
*
)
;
//__xi_a[], __xi_z[]; /* C initializers */
//__xc_a[], __xc_z[]; /* C++ initializers */
//__xp_a[], __xp_z[]; /* C pre-terminators */
//__xt_a[], __xt_z[]; /* C terminators */
extern
"C"
void
destructores
(
)
;
#define FALSE 0
#define TRUE 1
extern
"C"
void
mainCRTStartup
(
)
{
char
*
parametros
=
GetCommandLineA
(
)
;
char
*
temp
=
(
char
*
)
malloc
(
2048
)
;
memset
(
temp,
0
,
2048
)
;
__argc
=
0
;
__argv
=
(
char
**
)
malloc
(
4
)
;
char
Except
=
FALSE
;
while
(
*
parametros
)
{
if
(
*
parametros
==
34
)
{
if
(
Except
==
FALSE
)
{
Except
=
TRUE
;
}
else
{
Except
=
FALSE
;
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
}
parametros
++
;
continue
;
}
if
(
(
*
parametros
==
32
)
&&
(
Except
==
FALSE
)
)
{
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
}
else
{
unsigned
long
pos
=
strlen
(
temp
)
;
temp
[
pos
]
=
*
parametros
;
temp
[
pos
+
1
]
=
0
;
}
parametros
++
;
}
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
free
(
temp
)
;
_initterm
(
__xi_a,__xi_z
)
;
_initterm
(
__xc_a,__xc_z
)
;
main
(
__argc,__argv,
0
)
;
_initterm
(
&
__xp_a,
&
__xp_z
)
;
_initterm
(
&
__xt_a,
&
__xt_z
)
;
destructores
(
)
;
for
(
int
y
=
0
;
y
<
__argc
;
y
++
)
{
free
(
__argv
[
y
]
)
;
}
free
(
__argv
)
;
ExitProcess
(
0
)
;
}
static
void
__cdecl _initterm
(
_PVFV
*
pfbegin,_PVFV
*
pfend
)
{
while
(
pfbegin
<
pfend
)
{
if
(
*
pfbegin
)
{
(
**
pfbegin
)
(
)
;
}
++
pfbegin
;
}
}
Este nuevo main define las diferentes secciones donde se encontraran las benditas tablas, llama a las tablas de inicio justo antes que a main() y a las de terminaciones justo después, además de llamar a nuestra función destructores(). Las tablas son procesadas por la función initterm() que recorre la tablae de punteros a funciones, ejecutando cada uno de ellas, hasta encontrar un puntero nulo.
Creamos nuestra libreria y ESTATICOS.EXE:
cl /c *.cpp
lib *.obj /out:clib.lib
cl /c ESTATICOS.CPP
link ESTATICOS.OBJ /NODEFAULTLIB USER32.LIB KERNEL32.LIB CLIBCLIB.LIB
Ahora si, tenemos el mismo comportamiento que con la Run Time Library de VC++ para los objetos estáticos.
Un regalito en esta parte, vamos a ver como manejar las variables de entorno:
C_MAIN.CPP
Código: C++
#include <malloc.h>
#include <string.h>
//entorno
extern
"C"
int
vars
=
0
;
extern
"C"
char
**
_environ
=
0
;
extern
"C"
char
*
__stdcall GetEnvironmentStrings
(
void
)
;
extern
"C"
bool
__stdcall FreeEnvironmentStringsA
(
char
*
block
)
;
void
__cdecl _envinit
(
void
)
;
//parametros
extern
"C"
int
__argc
=
1
;
extern
"C"
char
**
__argv
=
0
;
extern
"C"
void
__stdcall ExitProcess
(
unsigned
long
uExitCode
)
;
extern
"C"
char
*
__stdcall GetCommandLineA
(
void
)
;
extern
int
main
(
int
argc,
char
*
argv
[
]
,
char
*
env
[
]
)
;
extern
"C"
int
__stdcall MessageBoxA
(
int
a,
char
*
b,
char
*
c,
unsigned
int
p
)
;
typedef
void
(
__cdecl
*
_PVFV
)
(
void
)
;
#pragma data_seg(".CRT$XIA")
_PVFV __xi_a
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XIZ")
_PVFV __xi_z
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XCA")
_PVFV __xc_a
[
]
=
{
0
}
;
#pragma data_seg(".CRT$XCZ")
_PVFV __xc_z
[
]
=
{
0
}
;
typedef
void
(
*
PFV
)
(
void
)
;
#pragma data_seg(".CRT$XPA")
PFV __xp_a
=
0
;
/* C pre-terminators */
#pragma data_seg(".CRT$XPZ")
PFV __xp_z
=
0
;
#pragma data_seg(".CRT$XTA")
PFV __xt_a
=
0
;
/* C terminators */
#pragma data_seg(".CRT$XTZ")
PFV __xt_z
=
0
;
extern
"C"
void
__cdecl _initterm
(
_PVFV
*
, _PVFV
*
)
;
//__xi_a[], __xi_z[]; /* C initializers */
//__xc_a[], __xc_z[]; /* C++ initializers */
//__xp_a[], __xp_z[]; /* C pre-terminators */
//__xt_a[], __xt_z[]; /* C terminators */
extern
"C"
void
destructores
(
)
;
#define FALSE 0
#define TRUE 1
extern
"C"
void
mainCRTStartup
(
)
{
char
*
parametros
=
GetCommandLineA
(
)
;
char
*
temp
=
(
char
*
)
malloc
(
2048
)
;
memset
(
temp,
0
,
2048
)
;
__argc
=
0
;
__argv
=
(
char
**
)
malloc
(
4
)
;
char
Except
=
FALSE
;
while
(
*
parametros
)
{
if
(
*
parametros
==
34
)
{
if
(
Except
==
FALSE
)
{
Except
=
TRUE
;
}
else
{
Except
=
FALSE
;
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
}
parametros
++
;
continue
;
}
if
(
(
*
parametros
==
32
)
&&
(
Except
==
FALSE
)
)
{
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
}
else
{
unsigned
long
pos
=
strlen
(
temp
)
;
temp
[
pos
]
=
*
parametros
;
temp
[
pos
+
1
]
=
0
;
}
parametros
++
;
}
if
(
strlen
(
temp
)
>
0
)
{
__argv
=
(
char
**
)
realloc
(
__argv,
4
*
(
__argc
+
1
)
)
;
__argv
[
__argc
]
=
(
char
*
)
malloc
(
strlen
(
temp
)
+
1
)
;
memset
(
__argv
[
__argc
]
,
0
,
strlen
(
temp
)
+
1
)
;
strcpy
(
__argv
[
__argc
]
,temp
)
;
strcpy
(
temp,
""
)
;
__argc
++
;
}
free
(
temp
)
;
_initterm
(
__xi_a,__xi_z
)
;
_initterm
(
__xc_a,__xc_z
)
;
_envinit
(
)
;
main
(
__argc,__argv,_environ
)
;
_initterm
(
&
__xp_a,
&
__xp_z
)
;
_initterm
(
&
__xt_a,
&
__xt_z
)
;
destructores
(
)
;
for
(
int
y
=
0
;
y
<
__argc
;
y
++
)
{
free
(
__argv
[
y
]
)
;
}
free
(
__argv
)
;
for
(
int
z
=
0
;
z
<
vars
;
z
++
)
{
if
(
_environ
[
z
]
)
{
free
(
_environ
[
z
]
)
;
}
}
free
(
_environ
)
;
ExitProcess
(
0
)
;
}
static
void
__cdecl _initterm
(
_PVFV
*
pfbegin,_PVFV
*
pfend
)
{
while
(
pfbegin
<
pfend
)
{
if
(
*
pfbegin
)
{
(
**
pfbegin
)
(
)
;
}
++
pfbegin
;
}
}
void
__cdecl _envinit
(
void
)
{
char
*
var
=
GetEnvironmentStrings
(
)
;
_environ
=
(
char
**
)
malloc
(
4
)
;
while
(
*
var
)
{
_environ
=
(
char
**
)
realloc
(
_environ,
(
vars
+
1
)
*
4
)
;
_environ
[
vars
]
=
(
char
*
)
malloc
(
strlen
(
var
)
+
1
)
;
memset
(
_environ
[
vars
]
,
0
,
strlen
(
var
)
+
1
)
;
strcpy
(
_environ
[
vars
]
,var
)
;
(
char
*
)
var
+
=
strlen
(
(
char
*
)
var
)
;
(
char
*
)
var
+
=
1
;
vars
++
;
}
FreeEnvironmentStringsA
(
var
)
;
}
Esta nueva versión del C_MAIN.CPP incorpora la función _envinit, que cre la lista de variables de entorno y le pasa la variable _environ a la función main() de nuestro programa.
Ahora vamos a implementar getenv y putenv:
C_PUTENV.CPP
Código: C++
#include <malloc.h>
#include <string.h>
extern
"C"
int
vars
;
extern
"C"
char
**
_environ
;
bool
compararPut
(
char
*
s1,
const
char
*
s2
)
{
while
(
*
s2
)
{
if
(
*
s1
!
=
*
s2
)
{
return
false
;
}
s1
++
;
s2
++
;
}
return
true
;
}
extern
"C"
int
putenv
(
const
char
*
envstring
)
{
for
(
int
x
=
0
;
x
<
vars
;
x
++
)
{
if
(
!
_environ
[
x
]
)
{
continue
;
}
if
(
compararPut
(
_environ
[
x
]
,envstring
)
==
true
)
{
char
*
ptr
=
(
char
*
)
envstring
;
while
(
*
envstring
)
{
if
(
(
*
ptr
==
'='
)
&&
(
*++
ptr
==
0
)
)
{
free
(
_environ
[
x
]
)
;
_environ
[
x
]
=
0
;
return
0
;
}
ptr
++
;
}
strcpy
(
_environ
[
x
]
,envstring
)
;
return
0
;
}
}
_environ
=
(
char
**
)
realloc
(
_environ,
(
vars
+
1
)
*
4
)
;
_environ
[
vars
]
=
(
char
*
)
malloc
(
strlen
(
envstring
)
+
1
)
;
memset
(
_environ
[
vars
]
,
0
,
strlen
(
envstring
)
+
1
)
;
strcpy
(
_environ
[
vars
]
,envstring
)
;
vars
++
;
return
0
;
}
C_GETENV.CPP
Código: C++
#include <string.h>
extern
"C"
int
vars
;
extern
"C"
char
**
_environ
;
bool
compararGet
(
char
*
s1,
const
char
*
s2
)
{
while
(
*
s2
)
{
if
(
*
s1
!
=
*
s2
)
{
return
false
;
}
s1
++
;
s2
++
;
}
if
(
*
s1
==
'='
)
{
return
true
;
}
return
false
;
}
extern
"C"
char
*
getenv
(
const
char
*
varname
)
{
for
(
int
x
=
0
;
x
<
vars
;
x
++
)
{
if
(
!
_environ
[
x
]
)
{
continue
;
}
if
(
compararGet
(
_environ
[
x
]
,varname
)
==
true
)
{
return
(
_environ
[
x
]
+
strlen
(
varname
)
+
1
)
;
}
}
return
0
;
}
Podemos probar su correcto funcionamiento con cualquier programa que use el parametro envp o simplemente cambiando variables del entorno como TMP (el path de los archivos temporales).
En la cuarta parte veremos como implementar las funciones de entrada y salida por consola.
Saludos,
Mariano.
Autor: Mariano Ventaja
http://www.c0d3rz.com.ar
Descarga:
http://c0d3rz.com.ar/foro/viewtopic.php?t=104
Tweet
Nacional y Popular
En mi país la bandera de Eva es inmortal.
Queremos una Argentina socialmente justa, económicamente libre y políticamente soberana.
¡Perón cumple, Evita dignifica!
La mano invisible del mercado me robo la billetera.
Imprimir
Páginas: [
1
]
« anterior
próximo »
SoloCodigo
»
Foros
»
Programación General
»
C/C++
(Moderador:
Eternal Idol
) »
[Artículo]
Crear nuestra propia Run Time Library (Tercera Parte)