Tema: Solicitud de ayuda sobre programación

gerardohuck - 25/2/2006 a las 00:38

Buenas, soy alumno de LCC y estoy haciendo (o intentando) hacer el trabajo final correspondiente a Arquitectura. Junto con un compañero hemos estado buscando info en internet y nos está bastante encontrar cosas concretas.
Primer ítem: no podemos encontar el código fuente correspondiente al archivo crt0.o o crtN.o, qué son archivos con los que se linkean a todos los programas cuando se compilan.
Segundo ítem: quisieramos sabér cómo se puede hacer para desde dentro de un programa en ejecución hecho en asemmbler, ejecutar a otro programa del que se conoce el nombre.
Bueno, cualquier ayuda nos significaría mucho
Gracias a todos
Gerardo Huck


duilio - 26/2/2006 a las 17:33

quote:
Segundo ítem: quisieramos sabér cómo se puede hacer para desde dentro de un programa en ejecución hecho en asemmbler, ejecutar a otro programa del que se conoce el nombre.


Respondo primero el segundo ítem así va introduciendo al tema de los archivos objeto, sus símbolos y el proceso de compilación y enlazado, que es lo que ustedes quieren comprender.

El camino simple es usar lo que ya está hecho:

code:

#include <stdlib.h>

int run (char *command)
{
system (command);
}



Si esto está en un archivo run.c:

> gcc -c run.c

ahora tienen el objeto run.o:

> nm run.o
00000000 T run
U system

como pueden ver, el objeto contiene un símbolo run en el segmento de texto y un símbolo system sin definir. Todo esto es muy claro cuando se ve el código C anterior:

- Se declara y define una función run(), con lo cual: el símbolo no está sin definir (no aparece como U); y el símbolo está en el segmento de texto (T), que es donde van los bytes correspondientes a código "ejecutable" (lo simplifico, la definición de código ejecutable es más compleja).
- Se declara, mediante el header stdlib.h, pero no se define, un símbolo system. Éste símbolo es parte de la biblioteca estándar de C. Si programan en Linux utilizando la implementación GNU de dicho estándar, dicho símbolo lo definirá la GNU LibC (glibc).

Es importante comprender la diferencia entre declarar un símbolo y definirlo. Ahora run() ya se puede utilizar desde código assembler, digamos en mycode.s, apilando en el stack la string correspondiente y haciendo la llamada. Por supuesto al enlazar, deberán incluir el objeto run.o que contiene la definición del símbolo correspondiente a run().

> gcc myothercode.o run.o -o myapp.exe

quote:
Primer ítem: no podemos encontar el código fuente correspondiente al archivo crt0.o o crtN.o, qué son archivos con los que se linkean a todos los programas cuando se compilan.


Estos archivos contienen código bootstrap que utiliza GCC para armar un ejecutable o biblioteca final.

Como se imaginarán, no es el sistema operativo el encargado de tomar un objeto binario ejecutable, localizar el símbolo main, y pasarle el control a éste con los argumentos necesarios y demás cosas de inicialización. Hacerlo así ataría al sistema operativo a un lenguaje de programación en particular.

En cambio, cada sistema operativo soporta al menos un formato de archivo binario. Linux soporta varios, el por defecto (y más complejo) es el ELF (Executable File Format). Windows utiliza PE (Portable Executable), derivado de COFF, más simple que el ELF y por ello más rápido de cargar.

Si observan dichos objetos:

> nm /usr/lib/crt1.o
00000000 D __data_start
00000000 W data_start
00000000 R _fp_hw
00000004 R _IO_stdin_used
U __libc_csu_fini
U __libc_csu_init
U __libc_start_main
U main
00000000 T _start

main está sin definir, claramente, ya que ustedes deben definirlo. El único segmento "ejecutable" definido es _start. Si observamos lo que hace:

> objdump -d /usr/lib/crt1.o

/usr/lib/crt1.o: formato del fichero elf32-i386

Desensamblado de la sección .text:

00000000 <_start>:
0: 31 ed xor %ebp,%ebp
2: 5e pop %esi
3: 89 e1 mov %esp,%ecx
5: 83 e4 f0 and $0xfffffff0,%esp
8: 50 push %eax
9: 54 push %esp
a: 52 push %edx
b: 68 00 00 00 00 push $0x0
10: 68 00 00 00 00 push $0x0
15: 51 push %ecx
16: 56 push %esi
17: 68 00 00 00 00 push $0x0
1c: e8 fc ff ff ff call 1d <_start+0x1d>
21: f4 hlt
22: 90 nop
23: 90 nop

es decir que se hace un call a un cierto lugar que aún no está definido (que no están en el segmento de texto de crt1.o). Pero al compilar todo un programa, digamos un hello world, se ve que todo cierra bien (sólo pongo las partes relevantes):

> objdump -d hello.exe

... muchas cosas

080482b8 <__libc_start_main@plt>:
80482b8: ff 25 60 95 04 08 jmp *0x8049560
80482be: 68 08 00 00 00 push $0x8
80482c3: e9 d0 ff ff ff jmp 8048298 <_init+0x18>

080482c8 <__gmon_start__@plt>:
80482c8: ff 25 64 95 04 08 jmp *0x8049564
80482ce: 68 10 00 00 00 push $0x10
80482d3: e9 c0 ff ff ff jmp 8048298 <_init+0x18>
Desensamblado de la sección .text:

080482d8 <_start>:
80482d8: 31 ed xor %ebp,%ebp
80482da: 5e pop %esi
80482db: 89 e1 mov %esp,%ecx
80482dd: 83 e4 f0 and $0xfffffff0,%esp
80482e0: 50 push %eax
80482e1: 54 push %esp
80482e2: 52 push %edx
80482e3: 68 08 84 04 08 push $0x8048408
80482e8: 68 ac 83 04 08 push $0x80483ac
80482ed: 51 push %ecx
80482ee: 56 push %esi
80482ef: 68 7c 83 04 08 push $0x804837c
80482f4: e8 bf ff ff ff call 80482b8 <__libc_start_main@plt>
80482f9: f4 hlt
80482fa: 90 nop
80482fb: 90 nop

... muchas más cosas (como frame_dummy, __libc_csu_init, __libc_csu_fini, __do_global_dtors_aux, __do_global_ctors_aux, _fini)

0804837c <main>:
804837c: 55 push %ebp
804837d: 89 e5 mov %esp,%ebp
804837f: 83 ec 08 sub $0x8,%esp
8048382: 83 e4 f0 and $0xfffffff0,%esp
8048385: b8 00 00 00 00 mov $0x0,%eax
804838a: 83 c0 0f add $0xf,%eax
804838d: 83 c0 0f add $0xf,%eax
8048390: c1 e8 04 shr $0x4,%eax
8048393: c1 e0 04 shl $0x4,%eax
8048396: 29 c4 sub %eax,%esp
8048398: 83 ec 0c sub $0xc,%esp
804839b: 68 5c 84 04 08 push $0x804845c
80483a0: e8 03 ff ff ff call 80482a8 <puts@plt>
80483a5: 83 c4 10 add $0x10,%esp
80483a8: c9 leave
80483a9: c3 ret

Las diferentes secciones, tablas y código de inicialización provienen de la inmensa complejidad del ELF. Todos esos símbolos y tablas extraños que se ven, se deben implementar para cumpliar con la ABI del formato ELF. Por suerte esto lo hacen los compiladores, ensambladores y enlazadores por nosotros. Aquí se empieza a ver por qué no es el SO el encargado de dicho trabajo. Si quieren ver algo en verdad enredado, échenle una mirada a un binario ejecutable que provenga de código C++ no trivial (como un programa en Qt).

Entonces, volviendo a tu pregunta, si aún te interesa el código fuente para los símbolos definidos en crt1.o y demás, lo podés encontrar en el código de la glibc. Por ejemplo, la definición de _start que vimos antes se puede ver en el CVS online:

http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/sysdeps/i 386/elf/?cvsroot=glibc

Para comprender a full todos los temas anteriores, vas a necesitar conocimientos de Sistemas Operativos. Te comento que SO con Guido es entre 5 y 10 veces más pesada que Arquitectura (en carga de trabajo), con lo cual te recomiendo fuertemente que trabajen mucho durante la cursada, ya que encima Guido en general no toma parciales, con lo cual los alumnos se relajan y se dedican a otras materias con mayores urgencias.


Saludos,
Duilio.


gerardohuck - 28/2/2006 a las 00:38

MUCHÍSIMAS GRACIAS!!!
La verdad es que aunque todavía no probé programar, es de una GRAN ayuda todo lo que escribiste... Es más, ya es la segunda vez que nos das una mano, así que si me entero de tu cumple tendré que hacer algún regalo!
Gracias de nuevo!
Gerardo Huck


Este tema viene de: www.exactas.org
http://www.exactas.org/

Dirección de este sitio:
http://www.exactas.org//modules.php?op=modload&name=XForum&file=viewthread&fid=21&tid=565