GCC COMO

dan@detached.demon.co.uk
Traducido por Borja Muñoz Aguilar, x4354281@turing.ugr.es)

v1.17, Traducción del 21 de Mayo de 1998.
Este documento trata acerca de la configuración del compilador GNU C y el desarrollo de librerías bajo Linux, y da una visión global de la compilación, el linkado, la ejecución y la depuración de programas bajo él. La mayoría del material expuesto aquí está sacado del GCC-FAQ de Mitch D'Souza's, al cual reemplaza, o del ELF-HOWTO, al que reemplazará algún día. Esta es la primera versión publicada (a pesar del número de la versión; cosas de RCS). Las consultas, respuestas, etc., serán bienvenidas.

1. Preliminares

1.1 ELF vs. a.out

El desarrollo bajo Linux está ahora mismo en un estado cambiante. Hay dos formatos para los archivos binarios que Linux sabe ejecutar, y dependiendo de cómo esté configurado su sistema, puede tener uno u otro. Leer este COMO le ayudará a saber cuál.

¿Cómo saberlo? Use la orden file (p.ej. file /bin/bash). Un programa ELF dará como respuesta algo con ELF dentro, para un programa a.out dirá algo que contenga Linux/i386.

Las diferencias entre ELF y a.out son cubiertas (extensivamente) posteriormente en este documento. ELF es el formato más reciente, y es generalmente aceptado como mejor.

1.2 Administrata

La información de copyright puede ser encontrada al final de este documento, junto con los avisos acerca de preguntar cosas obvias en Usenet, revelando tu ignorancia del lenguaje C mandando fallos que no son tales.

1.3 Tipografía

Si estás leyendo este documento en formato Postscript, dvi, o html, verás algunas variaciones en las fuentes con respecto a la versión de texto plano. En particular, los nombres de fichero, ordenes, salidas de ordenes y código fuente están en una fuente typewriter, mientras que las 'variables' que necesitan ser enfatizadas lo estan asi: variable.

También hay un índice. En dvi o postscript, los número del índice son números de secciones. En HTML son sólo número asignados secuencialmente sobre los que haces click. En la versión de texto plano, sólo son números. ¡Actualízate!

La sintaxis del Bourne shell (mejor que la del C) es la usada en los ejemplos. Los usuarios del C shell utilizarán

% setenv FOO bar
donde yo habria escrito
$ FOO=bar; export FOO

Si el prompt es # en vez de $, el comando sólo funcionará probablemente como root. Por supuesto, no acepto ninguna responsabilidad por nada que le ocurra a tu sistema como resultado de probar estos ejemplos. Que pases un buen día :-)

2. Dónde obtener lo necesario

2.1 Este documento

Este documento es uno de la serie Linux HOWTO, asi que está disponible en todos los sitios que mantienen Linux HOWTOs como http://sunsite.unc.edu/pub/linux/docs/HOWTO/

O ftp.insflug.org para las traducciones en Castellano
La versión HTML puede ser encontrada (posiblemente una versión más reciente) en http://ftp.linux.org.uk/~barlow/howto/gcc-howto.html.

2.2 Otra documentación

La documentación oficial para gcc está en la distribución fuente (ver abajo) como ficheros texinfo, y como ficheros .info. Si tiene una conexión de red rápida, un cdrom, o una cantidad razonable de paciencia, puede hacerles un untar y copiar los archivos relevantes en /usr/info. Si no, puedes encontrarlo en ftp://tsx-11.mit.edu/pub/linux/packages/GCC/, pero no necesariamente siempre la última versión.

Hay dos fuentes de documentación para libc. GNU libc viene con ficheros info que describen la libreria libc de Linux bastante bien si exceptuamos stdio. También, las páginas del manual ftp://sunsite.unc.edu/pub/Linux/docs/ están escritas para Linux y describen un montón de llamadas al sistema (sección 2) y funciones de libc (sección 3).

2.3 GCC

Hay dos respuestas.

(a) La distribución oficial de GCC para Linux puede ser encontrada siempre en formato binario (ya compilada) en ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/. En el momento de escribir esto, la última versión es la 2.7.2 (gcc-2.7.2.bin.tar.gz).

(b) La última distribución en código fuente de la Free Software Foundation puede ser encontrada en GNU archives. Esta no tiene porque ser necesariamente la misma versión que la de arriba, aunque ahora mismo sí. Las personas que mantienen el GCC de Linux se han esforzado en que sea fácil compilar la última versión --- el script lo hará todo por tí. Si quieres aplicar algun parche prueba también en tsx-11.

Para compilar las cosas que no sean triviales (y también para algunas triviales) necesitarás también:

2.4 La librería de C y los ficheros de cabecera

Lo que tu quieres aqui depende de si tu sistema es ELF o a.out, así como de lo que quieres que sea. Si estás actualizando de libc 4 a libc 5, sería recomendable que echarás un vistazo al ELF-HOWTO.

Están disponibles en tsx-11 como arriba:

libc-5.2.18.bin.tar.gz

--- Imágenes de las librerías compartidas de ELF, librerías estáticas y ficheros include para las librerías matemáticas y de C.

libc-5.2.18.tar.gz

--- Código fuente de lo de arriba. También necesitarás el paquete .bin. para los ficheros de cabecera. Si estás dudando entre compilar la librería de C tú mismo o usar los binarios, la respuesta correcta en la mayoría de los casos es usar los binarios. De cualquier manera necesitarás saber si quieres soporte para NYS o para password shadow.

libc-4.7.5.bin.tar.gz

--- Imágenes de las librerías compartidas a.out y librerías estáticas para la versión 4.7.5 de la librería de C y sus amigos. Está diseñado para coexistir con el paquete libc 5, pero sólo es realmente necesario si deseas seguir usando/desarrollando programas en formato a.out.

2.5 Herramientas asociadas (as, ld, ar, strings etc)

Desde tsx-11, como todo lo anterior. La versión actual es binutils-2.6.0.2.bin.tar.gz.

Las binutils sólo están disponibles en ELF, la versión actual de libc está en ELF y la versión de a.out se comporta mejor cuando se usa junto con la libc de ELF. El desarrollo de la librería de C se mueve según la pauta que marca ELF, y a menos que tengas razones realmente buenas para necesitar a.out debes decantarte por ELF.

3. Instalacion y Configuracion de GCC

3.1 Versiones de GCC

Puedes saber la version de GCC que estas ejecutando tecleando gcc -v en el prompt. Esta es también una manera fiable de saber si lo tienes configurado para ELF o para a.out. En mi sistema:

$ gcc -v
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs
gcc version 2.7.2

Podemos sacar la siguiente informacion:

Asi que, en resumen, tengo el gcc 2.7.2 produciendo codigo ELF. Vaya sorpresa.

3.2 ¿Donde está?

Si has instalado gcc sin vigilar el proceso, o si lo tienes como una parte de la distribucion, puede que quieras saber donde esta en el sistema de ficheros. La respuesta es la siguiente:

3.3 ¿Donde estan los ficheros de cabecera?

Aparte de lo que tu instales bajo /usr/local/include, hay tres fuentes principales de ficheros de cabecera en Linux.

3.4 Construyendo compiladores cruzados

Linux como plataforma de ejecutables

Suponiendo que tienes el codigo fuente para gcc, normalmente puedes seguir las instrucciones que se dan en el fichero INSTALL para GCC. Un configure --target=i486-linux --host=XXX en la plataforma XXX seguido de un make lo hara. Todo lo que necesitas son los includes de Linux, los includes del Kernel, y tambien para construir el ensamblador cruzado y el enlazador cruzado de los fuentes en ftp://tsx-11.mit.edu/pub/linux/packages/GCC/.

Linux como plataforma fuente, MSDOS como plataforma de ejecutables

Uff. Aparentemente esto es posible si se utiliza el paquete "emx" o el extensor "go". Mira en: ftp://sunsite.unc.edu/pub/Linux/devel/msdos. No lo he probado, asi que no puedo decir nada mas.

4. Portando codigo y compilando.

4.1 Simbolos automaticamente definidos

Puedes saber que simbolos define tu version de gcc automaticamente ejecutandolo con la opcion -v. Por ejemplo, la mia:

$ echo 'main(){printf("hello world\n");}' | gcc -E -v -
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs
gcc version 2.7.2
/usr/lib/gcc-lib/i486-box-linux/2.7.2/cpp -lang-c -v -undef
-D__GNUC__=2 -D__GNUC_MINOR__=7 -D__ELF__ -Dunix -Di386 -Dlinux
-D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__i386
-D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386)
-Amachine(i386) -D__i486__ -

Si estas escribiendo codigo que usa caracteristicas especificas de Linux es buena idea meter la parte no portable entre:

#ifdef __linux__
/* ... Lo que sea ... */
#endif /* linux */

Usa __linux__ para esto, nunca linux. Aunque esta definido no cumple las normas POSIX.

4.2 Invocacion del compilador

La documentacion sobre las opciones del compilador es la pagina info de gcc (en Emacs, usa C-h i y despues selecciona la opcion gcc). Tu distribuidor puede que no lo haya incluido en tu sistema, o puedes tener una version antigua; lo mejor es bajarse el archivo fuente de gcc de ftp://prep.ai.mit.edu/pub/gnu o uno de sus mirrors, y copiarla de ahi.

La pagina del manual de gcc (gcc.1), esta desfasada. Te avisara de esto cuando quieras consultarla.

Opciones del compilador

gcc puede optimizar el codigo que genera si añadimos -On a su linea de comandos, donde n es un entero pequeño opcional. Los valores de n y su efecto exacto varian dependiendo de la version exacta, pero tipicamente van desde 0 (sin optimizacion) a 2 (mucha) o 3 (aun mas).

Internamente gcc traduce esto a una serie de opciones -f y -m. Puedes ver exactamente cuales niveles -O se corresponden con que opciones ejecutando gcc con la opcion -v y la opcion (indocumentada) -Q. Por ejemplo, para -O2 el mio dice:

enabled: -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks
-fexpensive-optimizations
        -fthread-jumps -fpeephole -fforce-mem -ffunction-cse -finline
        -fcaller-saves -fpcc-struct-return -frerun-cse-after-loop
        -fcommon -fgnu-linker -m80387 -mhard-float -mno-soft-float
        -mno-386 -m486 -mieee-fp -mfp-ret-in-387

Si utilizas un nivel de optimizacion mayor que el que soporta tu compilador (p.ej. -O6) tendra exactamente el mismo efecto que si utilizaras el nivel mas alto que soporta. Distribuir codigo que esta definido para compilarse de esta manera no es una buena idea -- si se hacen posteriormente mas optimizaciones en versiones futuras, tu (o tus usuarios) te puedes encontrar con que tu codigo no funciona.

Los usuarios de gcc 2.7.0 hasta 2.7.2 se daran cuenta de que hay un error en -O2 en estas versiones. Especificamente, la reduccion de potencia no funciona. Se puede obtener un patch para solucionar esto si quieres recompilar gcc, en otro caso asegurate de que compilas siempre con -fno-strength-reduce.

Cuestiones especificas de procesadores

Hay otras opciones -m que no se activan con -O pero que son utiles. Las mas importantes son -m386 y -m486, que informan al gcc de que tiene que optimizar para 386 o 486 respectivamente. El codigo compilado con cualquiera de las dos funcionara en el otro procesador; el codigo para 486 es mas grande, pero no mas lento en el 386.

Actualmente no hay ninguna opcion -mpentium o -m586. Linus sugiere usar -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 para obtener codigo 486 optimizado pero sin los grandes huecos para asignacion (que el pentium no necesita). Michael Meissner (de Cignus) comenta lo siguiente:

-mno-strength-reduce tambien proporciona un codigo mas rapido en los x86 (no estoy hablando del fallo de reduccion de potencia, que es otro tema). Esto es asi porque los x86 no tienen precisamente un exceso de registros (y el metodo de GCC de agrupar registros en otros registros no ayuda tampoco). La reduccion de potencia tipicamente tiene como resultado el usar registros adicionales para reemplazar las multiplicaciones por sumas. Tambien sospecho que -fcaller-saves puede ser una perdida.
-fomit-frame-pointer puede o puede no ser algo beneficioso. Por un lado, puede significar que otro registro esta disponible para asignacion. Por otro, el modo en el que los x86 codifican su conjunto de instrucciones significa que las direcciones relativas a la pila ocupan mas espacio que las direcciones relativas al marco, lo que provoca que haya menos Icache disponible para el programa. Tambien, -fomit-frame-pointer hace que el compilador tenga que estar ajustando constantemente el puntero de pila despues de cada llamada, mientras que con un marco, deja a la pila acumular durante unas cuantas llamadas.

Los ultimos comentarios en este punto son tambien de Linus:

Si quieres obtener un rendimiento optimo, no te fies de mi: prueba. Hay muchas opciones para el compilador gcc, y puede que un determinado conjunto de ellas te proporcione los mejores resultados.

Internal compiler error: cc1 got fatal signal 11

La señal 11 es SIGSEGV, o "violacion de segmento". Normalmente significa que el programa ha hecho mal uso de sus punteros e intentado escribir a una zona de memoria que el no poseia. Asi que puede ser un fallo de gcc.

gcc es, de cualquier manera, una pieza de software bien testeada y digna de confianza, en su mayoria. Tambien utiliza un gran numero de estructuras de datos complejas, y un monton de punteros. En pocas palabras, es el programa de test de RAM agujereada disponible mundialmente. Si no puedes duplicar el fallo -- si no se para en el mismo lugar cuando reinicias la compilacion -- es casi seguro un problema con tu hardware (CPU, memoria, placa base o cache). No digas que es un fallo porque tu ordenador pase los tests de encendido o ejecute Windows perfectamente o cualquier otra cosa; estos "tests" se sabe que no sirven para nada. Y no digas que es un fallo porque una compilacion del kernel siempre se para durante el 'make zImage' -- ¡por supuesto lo hara! 'make zImage' esta probablemente compilando mas de 200 ficheros.

Si puedes duplicar el fallo, y (mejor) puedes hacer un pequeño programa que lo muestra, puedes mandarlo en un informe de fallo a la FSF, o a la lista de correo de linux-gcc. Consulta la documentacion de gcc para los detalles acerca de la informacion exacta que ellos necesitan.

4.3 Portabilidad

Se ha dicho, estos dias, que si algo no ha sido portado a Linux entonces no merece la pena tenerlo :-)

Aunque seriamente, en general solo se necesitan pequeños cambios para que los fuentes tengan compatibilidad 100% con las normas POSIX de Linux. Tambien merece la pena informar de cualquier cambio a los autores del codigo para que en el futuro solo se necesite 'make' para proporcionar un ejecutable que funcione.

Cuestiones BSD (incluyendo bsd_ioctl, daemon y <sgtty.h>)

Puedes compilar tu programa con -I/usr/include/bsd y linkarlo con -lbsd (p.ej. añade -I/usr/include/bsd a CFLAGS y -lbsd a LDFLAGS en tu Makefile). No hay necesidad de añadir -D__USE_BSD_SIGNAL si quieres un comportamiento con señales tipo BSD, porque lo obtienes automaticamente cuando pones _I/usr/include/bsd e incluyes <signal.h>.

Señales perdidas (SIGBUS, SIGEMT, SIGIOT, SIGTRAP, SIGSYS etc)(SIGBUS, SIGEMT, SIGIOT, SIGTRAP, SIGSYS etc)

Linux cumple con las normas POSIX. No hay señales definidas por POSIX ---ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), parrafo B.3.3.1.1 dice:

``Las señales SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS fueron omitidas de POSIX.1 porque su comportamiento es dependiente de la implementacion y no podia ser metido en una categoria adecuadamente. Las implementaciones pueden liberar estas señales pero deben documentar las circunstancias bajo las que son liberadas y hacer notar las circunstancias concernientes a esta liberacion.''

El modo mas facil para solucionarlo es redefinir estas señales a SIGUNUSED. El modo correcto es meter el codigo que las maneja entre #ifdefs apropiados:

#ifdef SIGSYS
/* ... el codigo no POSIX para SIGSYS va aqui .... */
#endif

Codigo K & R

GCC es un compilador ANSI; pero bastante codigo existente no es ANSI. No se puede hacer mucho con respecto a esto, excepto añadir -traditional a las opciones del compilador. Hay una cierta cantidad de controles acerca de que posibilidades emular; consulta la pagina info de gcc.

-traditional tiene otros efectos ademas de cambiar el lenguaje que gcc reconoce. Por ejemplo, cambia a -fwritable-strings, que mueve las constantes de cadena al espacio de datos (de el espacio de texto, al que no pueden ser escritas). Esto aumenta la huella que deja en la memoria el programa.

Los simbolos del preprocesador provocan conflictos con los prototipos en el codigo

Uno de los problemas mas frecuentes es que algunas funciones comunes estan definidas como macros en los ficheros de cabecera de Linux y el preprocesador no querra tener definiciones similares de prototipos en el codigo. Algunas comunes son atoi() y atol().

sprintf() Algo de lo que hay que estar avisado, especialmente cuando se porta desde SunOS, es que sprintf(string, fmt, ...) devuelve un puntero a cadena en varias unidades,mientras que Linux (siguiendo las normas ANSI) devuelve el numero de caracteresque fueron puestos en el string.

¿ FD_* ? fcntl y amigos. ¿ Donde estan las definiciones ?FD_* stuff ?

En <sys/time.h>. Si estas usando fcntl probablemente querras incluir <unistd.h> tambien, para el prototipo. En general, la pagina del manual para una funcion lista los #includes necesarios en su seccion SYNOPSIS.

El select() timeout. Programas que empiezan en espera ocupada.

Hubo un tiempo en el que el parametro timeout de select() se usaba solo para lectura. Incluso entonces la pagina del manual avisaba:

select() devolvera probablemente el tiempo restante del timeout original, si hay, modificando el valor del tiempo. Esto puede que se implemente en versiones futuras del sistema. Aunque, no es algo sabio asumir que el puntero timeout no sera modificado por la llamada select().

¡El futuro ha llegado! Al menos, esta aqui. Como vuelta de un select(), el argumento timeout toma el valor del tiempo restante que tendria que esperar hasta que lleguen los datos. Si no llegan, sera cero, y las llamadas futuras que usen la misma estructura timeout volveran inmediatamente.

Para solucionarlo pon el valor de timeout en esa estructura cada vez que llames a select(). Cambia el codigo

  
       struct timeval timeout;
       timeout.tv_sec = 1; timeout.tv_usec = 0;
       while (some_condition)
         select(n,readfds,writefds,exceptfds,&timeout);
por,
      
       struct timeval timeout;
       while (some_condition) {
         timeout.tv_sec = 1; timeout.tv_usec = 0;
         select(n,readfds,writefds,exceptfds,&timeout);
       }

Algunas versiones de Mosaic sufrieron este problema. La velocidad de la animacion del globo era inversamente proporcional a la velocidad a la que los datos llegaban de la red.

Llamadas al sistema interrumpidas.

Síntoma:

Cuando paramos un programa usando Ctrl-Z y lo reiniciamos - o en otras situaciones en las que se generan señales: interrupcion mediante Ctrl-C, terminación de un proceso hijo, etc. - el sistema se queja diciendo "interrupted system call" o "write: unknown error" o cosas asi.

Problema:

Los sistemas POSIX chequean si se han generado señales un poco más a menudo que algunos más antiguos. Linux puede ejecutar manejadores de señales en los siguientes casos:

Para otros sistemas operativos puede que tengas que incluir las llamadas al sistema creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop()creat(), a esa lista.

Si una señal (para la que el programa ha instalado un manejador) ocurre durante una llamada al sistema, se llama al manejador. Cuando el manejador vuelve (de la llamada al sistema) detecta que fue interrumpida, e inmediatamente devuelve -1 y errno = EINTR. El programa no espera que pase esto, con lo que acaba.

Puedes elegir entre dos soluciones.

(1) Para cada manejador de señal que instales, añade SA_RESTART a los flags de sigaction. Por ejemplo, cambia:

  signal (sig_nr, my_signal_handler);
a
  signal (sig_nr, my_signal_handler);
  { struct sigaction sa;
    sigaction (sig_nr, (struct sigaction *)0, &sa);
#ifdef SA_RESTART
    sa.sa_flags |= SA_RESTART;
#endif
#ifdef SA_INTERRUPT
    sa.sa_flags &= ~ SA_INTERRUPT;
#endif
    sigaction (sig_nr, &sa, (struct sigaction *)0);
  }

A pesar de que esto se aplica a la mayoria de llamadas al sistema, debes chequear si se devuelve EINTR en read(), write(), ioctl(), select(), pause() y connect(). Mira abajo.

(2) Chequear EINTR explicitamente:

Aqui hay dos ejemplos para read() e ioctl(),

Pieza original de codigo usando read()

int result;
while (len > 0) { 
  result = read(fd,buffer,len);
  if (result < 0) break;
  buffer += result; len -= result;
}

se convierte en

int result;
while (len > 0) { 
  result = read(fd,buffer,len);
  if (result < 0) { if (errno != EINTR) break; }
  else { buffer += result; len -= result; }
}

y un fragmento de codigo que usa ioctl()

int result;
result = ioctl(fd,cmd,addr);

se convierte en

int result;
do { result = ioctl(fd,cmd,addr); }
while ((result == -1) && (errno == EINTR));

En algunas versiones de UNIX BSD el comportamiento por defecto es reiniciar las llamadas al sistema. Para obtener llamadas al sistema interrumpidas tienes que usar el flag SV_INTERRUPT o SA_INTERRUPT.

Cadenas escribibles (el segmento del programa falla aleatoriamente)

GCC tiene una vision optimista de sus usuarios, creyendo que pretenden que las constantes de cadena sean exactamente eso -- constantes. Aunque las almacena en el area de texto (codigo) del programa , pueden ser paginadas fuera y dentro de la imagen en disco del programa (en vez de utilizar espacio de swap), y cualquier intento de reescribirlas causara un fallo de segmentacion.

Puede causar un problema para programas antiguos que, por ejemplo, llaman a mktemp() con una constante de cadena como argumento. mktemp() intenta reescribir su argumento.

Para solucionarlo, una de dos: (a) compila con -fwritable-strings, para que gcc ponga las constantes en el espacio de datos, o (b) reescribe el programa para asignar una cadena no constante y utilizar strcpy para copiarla en ella antes de la llamada.

¿Porque falla la llamada a execl()?

Porque no la llamas bien. El primer argumento de execl es el programa que quieres ejecutar. El segundo y los siguientes son el array argv[] del programa que estas llamando. Recuerda: argv[0] siempre contiene el nombre del programa incluso cuando se ejecuta sin argumentos. Asi que deberias escribir

  
execl("/bin/ls", "ls", NULL);

no solo

  
execl("/bin/ls", NULL);

Ejecutar el programa sin argumentos es una invitacion a que imprima sus dependencias de librerias dinamicas, al menos usando a.out. ELF hace otras cosas. (Si quieres esta informacion acerca de la libreria, hay otras maneras mas simples de conseguirlo: mira la seccion de carga dinamica, o la pagina del manual para ldd).

5. Depurando y perfilando.

ftp://larch.lcs.mit.edu/pub/Larch/lclint. No se como de bueno es.

5.2 Depuracion.

¿Como introduzco la informacion de depuracion en un programa?

Necesitas compilar y enlazar todas sus partes con la opcion -g, y sin la opcion -fomit-frame-pointer. No necesitas recompilarlo entero, solo las partes que estes interesado en depurar.

En configuraciones a.out las librerias compartidas estan compiladas con -fomit-frame-pointer, asi que gdb no trabajara con ellas. Si le ponemos la opcion -g cuando linkamos deberia implicar un linkado estatico, esa es la explicacion.

Si el linkador falla y devuelve un mensaje diciendo que no encuentra libg.a, no tienes /usr/lib/libg.a que es la libreria especial de C que activa la depuracion. Puedes encontrarla junto los binarios del paquete libc, o (en versiones mas reciente de la libreria C) puede que necesites obtener el codigo fuente de libc y compilarlo tu mismo. En realidad no lo necesitas; puedes obtener suficiente informacion para la mayoria de las situaciones simplemente creando un enlace simbolico a /usr/lib/libc.a

¿Como la saco ahora?

Mucho software GNU viene preparado para compilar y linkar con -g, provocando ejecutables muy grandes. No es una gran idea.

Si el programa tiene un script de configuracion que genera un autoconf, puedes normalmente eliminar la informacion de depuracion chequeando el fichero Makefile. Por supuesto, si usas ELF, el programa se enlaza dinamicamente, asi que solo puedes "destriparlo".

Software disponible

La mayoria de la gente usa gdb, puedes obtener los fuentes de Archivos GNU, o los binarios de tsx-11 o sunsite. xxgdb es un depurador X basado en el anterior (necesitas tener instalado gdb). El codigo fuente puede encontrarse en ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz.

Tambien, el depurador UPS ha sido portado por Rick Sladkey. Se ejecuta tambien bajo X, pero, a diferencia de xxgdb, no es una adaptacion de un depurador de modo texto. Tiene bastantes caracteristicas interesantes, y si gastas un poco de tiempo depurando, probablemente deberias probarlo. La version precompilada para Linux y los patches para los fuentes pueden encontrarse en ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/, y el codigo original en ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z.

Otra herramienta que te puede ser util para depurar es 'strace', que visualiza las llamadas al sistema que efectua un proceso. Tiene tambien muchos otros uso, incluyendo saber que nombres de rutas fueron compilado en binarios para los que no tienes el codigo fuente, "irritando" las condiciones de carrera en programas que supones que las contienen, y generalmente aprender como trabaja. La ultima version se puede encontrar en ftp://ftp.std.com/pub/jrs/.

Programas que se ejecutan en segundo plano (demonios).

Los demonios tipicamente ejecutan fork() tempranamente, y terminan el padre. Se hace esto para una corta sesion de depuracion.

La manera mas simple de atajar esto es poner un punto de ruptura para fork, y cuando el programa pare, forzarlo a devolver cero.

(gdb) list 
1       #include <stdio.h>
2
3       main()
4       {
5         if(fork()==0) printf("child\n");
6         else printf("parent\n");
7       }
(gdb) break fork
Breakpoint 1 at 0x80003b8
(gdb) run
Starting program: /home/dan/src/hello/./fork 
Breakpoint 1 at 0x400177c4

Breakpoint 1, 0x400177c4 in fork ()
(gdb) return 0
Make selected stack frame return now? (y or n) y
#0  0x80004a8 in main ()
    at fork.c:5
5         if(fork()==0) printf("child\n");
(gdb) next
Single stepping until exit from function fork, 
which has no line number information.
child
7       }

Ficheros core.

Cuando Linux arranca normalmente esta configurado para no producir ficheros core. Si te gustan usa el comando de shell para reactivarlos: para shells compatibles con el C-shell (p.ej. tcsh) se hace esto:

% limit core unlimited

mientras que para los shell como el Bourne (sh, bash, zsh, pdksh) se usa:

$ ulimit -c unlimited

Si quieres mas versatilidad en el nombre que se le da al fichero core (por ejemplo, si estas intentando hacer un post-mortem usando un depurador que tiene fallos) puedes hacer un mod a tu kernel. Mira el codigo de fsbinfmt_aout.c/ y fsbinfmt_elf.c/ (en los kernels mas recientes, tienes que hacer un grep) que dice:

        memcpy(corefile,"core.",5);
#if 0
        memcpy(corefile+5,current->comm,sizeof(current->comm));
#else
        corefile[4] = '\0';
#endif

y cambia los ceros por unos.

5.3 Perfilando. Perfilar es una manera de examinar que partes de un programa se llaman mas veces o se ejecutan mas tiempo. Es una buena manera de optimizar codigo y mirar en que se gasta el tiempo. Debes compilar todos los ficheros objeto que requiere informacion del tiempo con -p, y necesitaras tambien gprof (del paquete binutils). Mira la pagina del manual para gprof para los detalles.

6. Enlazando.

Entre los dos formatos binarios incompatibles, la distincion entre librerias estaticas y compartidas, y el significado doble para enlazar de "lo que ocurre despues de la compilacion" y "lo que ocurre cuando un programa compilado es invocado", hacen esta seccion complicada. Hay poco mas complicado que la frase anterior, aunque, no te preocupes mucho.

Para aliviar la confusion, nos referiremos a lo que ocurre en tiempo de ejecucion como "carga dinamica" y lo dejaremos para la proxima seccion. Tambien lo veras descrito como "enlazado dinamico", pero no aqui. Esta seccion esta dedicada exclusivamente a la clase de enlazado que ocurre al final de la compilacion.

6.1 Librerias compartidas vs librerias estaticas.

La ultima etapa en la construccion de un programa es enlazarlo; unir todas las piezas y ver que es lo que se ha perdido. Obviamente hay algunas cosas que muchos programas querran hacer -- abrir ficheros, por ejemplo, y las piezas que proveen estos servicios estan en forma de librerias. En Linux se encuentran sobre todo en /lib y en /usr/lib, ademas de en otros sitios.

Cuando se utiiza una libreria estatica, el enlazador encuentra lo que necesita los modulos del programa, y los copia fisicamente en el fichero ejecutable que genera. Para las librerias compartidas, no lo hace -- en vez de eso deja una nota en la salida diciendo "cuando se ejecute este programa, primero tendra que cargar esta libreria". Obviamente las librerias compartidas tienden a hacer ejecutables mas pequeños; tambien utilizan menos memoria y menos espacio en disco. El comportamiento por defecto de Linux es enlazar con librerias compartidas si las encuentra, y si no con las estaticas. Si tienes los binarios estaticos y los quieres compartidos, chequea que los ficheros para las librerias compartidas (*.sa para a.out, *.so para ELF) estan donde deberian esta, y tienen permiso de lectura.

En Linux, las librerias estaticas tienen nombres como nombre_de_libreria.a, mientras que las compartidas se llaman nombre_de_libreria.so.x.y.z donde x.y.z es un numero de version. Las librerias compartidas tienen a menudo enlaces apuntando a ellas, que son importantes, y (en configuraciones a.out) ficheros .sa asociados. Las librerias estandar vienen en los dos formatos, compartidas y estaticas.

Puedes saber que librerias compartidas requiere un programa usando ldd (Listar Dependencias Dinamicas)

$ ldd /usr/bin/lynx
       libncurses.so.1 => /usr/lib/libncurses.so.1.9.6
       libc.so.5 => /lib/libc.so.5.2.18

En mi sistema el browser para WWW 'linx' depende de la presencia de libc.so.5 (la libreria C) y libncurses.so.1 (usado para control de terminales). Si un programa no tiene dependencias, ldd dira "statically linked (enlazado estaticamente)" o "statically linked (ELF)".

6.2 Preguntando a las librerias ("¿En que libreria esta sin()?")

nm nombre_de_libreria deberia listar todos los simbolos a los que ese nombre_de_libreria tiene referencias. Trabaja tanto con librerias estaticas como con compartidas. Supon que quieres saber donde esta definidad tcgetattr(), puedes hacer:

 
$ nm libncurses.so.1 |grep tcget
U tcgetattr

La U quiere decir "undefined (no definido)" --- quiere decir que la libreri ncurses la usa pero no la define. Tambien puedes hacer:

$ nm libc.so.5 | grep tcget
00010fe8 T __tcgetattr
00010fe8 W tcgetattr
00068718 T tcgetpgrp

La W quiere decir debil, lo que significa que el simbolo esta definido, pero de tal manera que puede ser sobreescrito por otra definicion en una libreria diferente. Una definicion normal (como la que hay para que tcgetpgrp) esta marcada con una T.

La respuesta para la pregunta del titulo, es libm.(so|a). Todas las funciones definidas en <math.h> se guardan en la libreria de matematicas; aunque necesitaras enlazarlas con -lm cuando uses alguna.

6.3 Encontrando ficheros

ld: Output file requires shared library 'libfoo.so.1' (ld : fichero de salida requiere libreria compartida 'libfoo.so.1')

La estrategia de busqueda de ficheros de ld y sus amigos varia dependiendo de la version, pero el unico sitio por defecto en el que buscan es /usr/lib. Si quieres que busquen librerias en otros lugares, especifica sus directorios con la opcion -L en gcc o ld.

Si eso no ayuda, chequea que tengas el fichero correcto en ese lugar. Para a.out, enlazar con -lfoo provoca que ld busque libfoo.sa (la compartida), y si no tiene exito libfoo.a (la estatica). Para ELF, busca libfoo.so y entonces libfoo.a. libfoo.so es normalmente un enlace simbolico a libfoo.so.x.

6.4 Construyendo tus propias librerias

Control de versiones

Como cualquier otro programa, las librerias tienden a tener fallos que se solucionan con el tiempo. Tambien pueden introducir nuevas caracteristicas, cambiar el efecto de algunas existentes, o borrar las antiguas. Esto puede ser un problema para los programas que las usen; ¿que pasa si algo dependia de una caracteristica antigua?

Asi que, introducimos control de versiones para librerias. Podemos dividir los cambios que se hacen a una libreria en "grandes" o "pequeños", y decimos que un cambio "pequeño" no puede hacer que programas antiguos que usen una libreria no se ejecuten correctamente. Puedes saber la version de una libreria mirando el nombre de su fichero: libfoo.so.1.2 tiene numero de version mayor 1 y menor 2. El numero de version menor puede ser cualquiera -- libc pone nombres de librerias como libc.so.5.2.18, y es tambien razonable poner letras, guiones de subrayando, o cualquier caracter ASCII imprimible.

Una de las grandes diferencias entre ELF y a.out es la manera de construir librerias compartidas. Echemos un vistazo primero a ELF, porque es mas simple.

¿ELF? ¿Que es?

ELF (Executable and Linking Format (formato de ejecutable y enlazado) es un formato binario desarrollado originalmente por USL (Unix System Laboratories) y usado actualmente en Solaris y System V Release 4. Por su flexibilidad mejorada respecto al formato antiguo a.out que usaba Linux, los desarrolladores de librerias GCC y C decidieron el año pasado utilizar ELF como el formato estandar binario tambien.

¿Otra vez?

Esta seccion se ha cogido del documento '/news-archives/comp.sys.sun.misc'.

ELF es el "nuevo, mejorado" formato de ficheros objeto introducido en SVR4. ELF es mucho mas potente que COFF. ELF ve un fichero objeto como una lista arbitrariamente larga de secciones (no como un array de entidades de tamaño fijo), estas secciones, no como en COFF, no tienen que estar en un determinado lugar y no tienen que tener un orden especifico, etc. Los usuarios pueden añadir nuevas secciones a los ficheros objeto si desean capturar nuevos datos. ELF tambien tiene un nuevo formato de depuracion mas potente llamado DWARF (Debugging with Attribute Record Format (Depuracion con formato de registros de atributos) - no totalmente soportado por Linux en la actualidad. Una lista enlazada de DWARF DIEs (o entradas con informacion de depuracion) forma la seccion .debug en ELF. En vez de ser una coleccion de registros de informacion pequeños, de tamaño fijo, cada uno de los DWARF DIEs contiene una lista arbitrariamente larga de atributos complejos y estan escritos como un arbol de datos del programa. Los DIEs pueden capturar una gran cantidad de informacion que la seccion .debug de COFF simplemente no podia.
Los ficheros ELF son accedidos mediante la libreria de acceso a ELF de SVR4 (¿Solaris 2.0?), que provee un interfaz facil y rapido para las peores partes de ELF. Una de las mayores innovaciones usando la libreria de acceso a ELF es que nunca necesitaras mirar a un fichero ELF como un fichero UNIX, se accede como Elf *, despues de una llamada elf_open() y a partir de entonces, efectuas llamadas elf_foobar() en sus componentes en vez de a su imagen de disco (algo que muchos COFFers hacian con impunidad).

Las ventajas y desventajas de ELF, y las condiciones necesarias para actualizar un sistema a.out para soportarlo, se cubren en el ELF-HOWTO y no me he propuesto hacer un cut-paste aqui. El HOWTO deberia estar disponible en el mismo lugar en el que encontraste este.

Librerías compartidas de ELF

Para construir libfoo.so como una libreria compartida, los pasos basicos son estos:

$ gcc -fPIC -c *.c
$ gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o
$ ln -s libfoo.so.1.0 libfoo.so.1
$ ln -s libfoo.so.1 libfoo.so
$ LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH

Esto generara una libreria compartida llamada libfoo.so.1.0, y los enlaces apropiados para ld (libfoo.so) y el cargador dinamico (libfoo.so.1) para que puedan encontrarla. Para testearlo, añadimos el directorio actual al LD_LIBRARY_PATH.

Cuando veas que la libreria funciona, tendras que moverla a, digamos, /usr/local/lib, y volver a crear los links apropiados. El enlace de libfoo.so.1 a libfoo.so.1.0 es mantenido por ldconfig, que se ejecuta en la mayoria de los sistemas como una parte del proceso de arranque. El enlace a libfoo.so debe ser actualizado manualmente. Si eres escrupuloso acerca de actualizar todas las partes de una libreria (p.ej. los ficheros de cabecera) a la vez, lo mas simple de hacer es hacer que apunte libfoo.so a libfoo.so.1, de manera que ldconfig mantendra ambos enlaces por ti. Si no lo haces, puede que te ocurran toda clase de cosas mas tarde. Luego no digas que no te avisaron.

$ su
# cp libfoo.so.1.0 /usr/local/lib
# /sbin/ldconfig
# ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so )

Numeracion de versiones, sonames y enlaces simbolicos.

Cada libreria tiene un soname (N.T. No sabia como traducirlo, asi que lo mejor era dejarlo asi). Cuando el enlazador encuentra uno en una libreria que esta buscando, incrusta el soname en el binario en vez de el nombre del fichero. En tiempo de ejecucion, el cargador dinamico buscara un fichero con el nombre del soname, no el nombre el fichero de la libreria. Asi una libreria llamada libfoo.so podria tener como soname libbar.so, y todos los programas que se enlacen a ella buscaran libbar.so cuando empiecen.

Esto parece una desventaja, pero es la clave para entender como multiples versiones de la misma libreria pueden coexistir en un sistema. La manera de nombrar estandar para las librerias en Linux es llamar a la libreria, digamos, libfoo.so.1.2, y darle un soname como libfoo.so.1. Si se añade a un directorio estandar de librerias (p.ej. /usr/lib), ldconfig creara un enlace simbolico: libfoo.so.1 -> libfoo.so.1.2 para que la imagen apropiada se encuentre en tiempo de ejecucion. Tambien necesitaras crear un enlace libfoo.so -> libfoo.so.1 para que ld encuentre el soname correcto a utilizar en tiempo de ejecucion.

Asi que, cuando soluciones los errores de la libreria, o añadas nuevas funciones (cualquier cambio que no afecte a programas existentes), la reconstruyes, mantienes el soname como estaba, y cambias el nombre del fichero. Cuando haces cambios a la libreria que afectan a binarios existentes, simplemente incrementamos el numero del soname -- en este caso, llamamos a la nueva version libfoo.so.2.0, y le damos el soname libfoo.so.2. Ahora cambiamos el enlace de libfoo.so para que apunte a la nueva version y ya esta todo como antes.

Observa que no tienes que nombrar las librerias de esta manera, pero es una buena convencion. ELF te da flexibilidad para nombrar las librerias de modos que confundirian a un monton de gente, pero eso no quiere decir que tengas que usarlos.

Suponiendo que creas en la tradicion de que grandes actualizaciones pueden acabar con la compatibilidad, y cambios menores puede que no, entonces enlaza con

gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor

y todo estara correcto.

a.out. El formato tradicional.

La facilidad de construir librerias compartidas es una de las mayores razones para actualizarse a ELF. Aunque es posible hacerlo en a.out. Consigue ftp://tsx-11.mit.edu/pub/linux/packages/GCC/src/tools-2.17.tar.gz y lee el documento de 20 paginas que encontrarlas despues de descomprimirlo.

ZMAGIC vs QMAGIC

QMAGIC es un formato ejecutable igual que el antiguo a.out (tambien conocido como ZMAGIC), pero que deja la primera pagina sin mapear. Esto permite atrapar mas facil una desreferenciacion NULL ya que no existe el mapeo en el rango 0-4096. Como efecto lateral tus binarios seran mas pequeños (1 K).

Los enlazadores obsoletos soportan solo ZMAGIC, los semi-obsoletos soportan ambos formatos, y las versiones antiguas soportan solo QMAGIC. Esto no importa, ya que el kernel puede ejecutar ambos formatos.

El comando 'file' debe ser capaz de identificar un programa QMAGIC.

Colocacion de ficheros

Una libreria compartida a.out (DLL) consiste en dos ficheros reales y un enlace simbolico. Para la libreria 'foo' usada a lo largo de este documento como ejemplo, estos ficheros serian libfoo.sa y libfoo.so.1.2; el enlace simbolico seria libfoo.so.1 y apuntaria al mas reciente de los archivos. ¿Para que son?

En tiempo de compilacion, ld busca libfoo.sa. Es es el fichero stub para la libreria, y contiene todos los datos y punteros exportados a las funciones requeridas para el enlace en tiempo de ejecucion.

En tiempo de ejecucion, el cargador dinamico busca libfoo.so.1. Es un enlace simbolico asi que las librerias pueden ser actualizadas con versiones mas recientes sin fallos sin que nos carguemos ninguna aplicacion que se estuviese ejecutando a la vez. Despues de que la nueva version -- digamos, libfoo.so.1.3 --- se incluya, cuando ejecutemos ldconfig cambiara el enlace para que apunte a ella en una operacion atomica, dejando cualquier programa que tuviera la version antigua sin afectar.

Las librerias DLL son a menudo mas grandes que sus correspondientes estaticas. Reservan espacio para futuras expansiones en forma de huecos que pueden hacerse de manera que no ocupen espacio en disco. Una simple llamada cp o usar el programa makehole lograra esto. Tambien puedes destriparlas despues de construirlas, ya que las direcciones estan en posiciones fijas. No intentes destripar las librerias ELF.

¿"libc-lite"?

Una libc-lite es una version mas pequeña de la libreria libc construida de manera que cabe en un disquette y es suficiente para la mayoria de las tareas UNIX. No incluye curses, dbm, termcap, etc. Si tu /lib/libc.so.4 esta enlazada a una libreria lite, deberias reemplazarla con una version completa.

Enlazado : problemas comunes

!Mandame tus problemas de enlazado! Probablemente no los solucionare pero los escribire si tengo bastante...

Los programas se enlazan estaticamente a pesar de que tu los quieres compartidos

Chequea que tienes los enlaces correctos para que ld encuentre cada libreria compartida. Para ELF esto significa que libfoo.so esta enlazado simbolicamente a la imagen, para a.out un fichero libfoo.sa. Mucha gente tiene este problema despues de actualizar las binutils de ELF 2.5 A 2.6 -- la version mas antigua buscaba mas "inteligentemente" las librerias compartidas, de manera que no tenian que crear todos los enlaces. El comportamiento inteligente fue rechazado para mantener la compatibilidad con otras arquitecturas, y porque frecuentemente causaba mas problemas de los que solucionaba.

La herramienta DLL 'mkimage' no encuentra libgcc

A partir de libc.so.4.5.x libgcc deja de ser compartido. Asi que debes reemplazar todas las ocurrencias de '-lgcc' con 'gcc -print-libgcc-file-name'.

Tambien, borra todos los archivos /usr/lib/libgcc*. Esto es importante.

Multiples mensajes con __NEEDS_SHRLIB_libc_4

son otra consecuencia del mismo problema.

¿ Se visualiza el mensaje "Assertion failure" cuando reconstruyes una DLL?

Este criptico mensaje significa probablemente que uno de tus jump table slots ha causado un desbordamiento porque se habia reservado muy poco espacio en el fichero original jump.vars. Puedes encontrar los culpables ejecutando el comando 'getsize' que se encuentra junto en el paquete tools-2.17.tar.gz. Probablemente la unica solucion, es obligar al numero de version mayor a que sea incompatible con todo lo anterior.

ld: output file needs shared library libc.so.4 (ld: fichero de salida necesita libreria compartida libc.so.4)

Esto ocurre usualmente cuando estas enlazando con otras librerias que no son libc (p.ej. librerias X) y usas la opcion -g al enlazar sin usar -static.

Los stubs .sa para las librerias compartidas tienen usualmente un simbolo no definido _NEEDS_SHRLIB_libc_4 que es resuelto por el stub libc.sa. De todos modos con -g acabas enlazando con libg.a o libc.a y no se resuelva nunca este simbolo, haciendo que se imprime el mensaje de error anterior.

En conclusion, añade -static cuando compiles con la opcion -g, o no enlaces con -g. A menudo puedes obtener suficiente informacion de depuracion compilando los ficheros individualmente con -g, y enlazandolos sin -g.

7. Carga Dinamica

Esta seccion es un poco corta; la ire haciendo mas grande.

7.1 Conceptos Linux tiene librerias compartidas, aunque tu debes estar harto de oir esto si has leido la ultima seccion entera. Una parte del trabajo de hacer que los nombres se correspondan con las localizaciones que se hacia tradicionalmente en tiempo de enlazado se hace ahora en tiempo de ejecucion.

7.2 Mensajes de error

¡Mandame tus errores de enlazado! No los solucionare pero los presentare aqui...

can't load library: /lib/libxxx.so, Incompatible version No puedo cargar libreria: /lib/libxxx.so, version incompatible

(Solo a.out) Esto significa que no tienes el numero mayor correcto de la version de la libreria xxx. No, no puedes solamente hacer un enlace simbolico a otra version que tengas; si tienes suerte esto hara que tu programa provoque un fallo de segmentacion. Consigue la nueva version. Una situacion similar en ELF hara que aparezca un mensaje como este:

ftp: can't load library 'libreadline.so.2'

warning using incompatible library version xxx aviso usando version incompatible de libreria xxx

(Solo a.out) Tienes un numero de version menor de la libreria que el que uso la persona que compilo el programa. El programa se ejecutara. Probablemente. De todas maneras, una actualizacion no hace daño a nadie.

7.3 Controlando la operacion de carga dinamica

Hay una serie de variables de entorno a las que el cargador dinamico respondera. La mayoria de ellas se usan mas por ldd que por el usuario, y pueden ser fijadas mas convenientemente ejecutando ldd con algunas opciones. Incluyen:

7.4 Escribiendo programas con carga dinamica

Es muy parecido a la manera en que el soporte para carga dinamica de Solaris 2.x funciona, si eres familiar con el. Se habla sobre el extensamente en el documento de programacion ELF de H J Lu, y en la pagina del manual de dlopen(3), que puede ser encontrada en el paquete ld.so. Aqui hay un bonito ejemplo: enlazalo con -ldl

#include <dlfcn.h>
#include <stdio.h>

main()
{
  void *libc;
  void (*printf_call)();

  if(libc=dlopen("/lib/libc.so.5",RTLD_LAZY))
  {
    printf_call=dlsym(libc,"printf");
    (*printf_call)("hello, world\n");
  }

}

8. Contactar con los autores

8.1 Informes de fallos

Empieza por acotar el problema. Si es especifico de Linux, o ocurre con gcc en otros sistemas. Si es especifico de una version del kernel. Version de la libreria. Si ocurre cuando enlazas estaticamente. Si puedes hacer que el programa se ejecute de manera que demuestre el fallo.

Una vez que has hecho esto, sabras en que programas esta el fallo. Para GCC, el metodo para informar acerca de los fallos esta explicado en el fichero info. Para ld.so o las librerias C o de matematicas, envia el correo a linux-gcc@vger.rutgers.edu. Si es posible incluye un pequeño programa que exhiba el fallo, y una descripcion de lo que tu quieres que haga y lo que hace realmente.

8.2 Ayuda en el desarrollo

Si quieres ayudar en el desarrollo de GCC o de la libreria C, lo primero que tienes que hacer es unirte a la lista de correo de linux-gcc@vger.rutgers.edu. Si lo unico que quieres es saber por donde va la discusion, hay unos archivos en http://homer.ncm.com/linux-gcc/. El resto de las cosas a hacer dependen de lo que tu quieras hacer.

9. Final

9.1 Los creditos

Este HOWTO esta muy basado en el GCC-FAQ de Mitchum DSouza; la mayoria de la informacion (por no decir una cantidad razonable del texto) viene directamente de ese documento. Cualquier uso de la primera persona en este texto puede referirse tanto a el como a mi; generalmente las frases que dicen ''No he testeado esto; no te enfades si esto se carga tu disco duro'' se aplican a nosotros dos.

Las personas que han contribuido a la realizacion de este documento son (en orden ASCII): Andrew Tefft, Axel Boldt, Bill Metzenthen, Bruce Evans, Bruno Haible, Daniel Barlow, Daniel Quinlan, David Engel, Dirk Hohndel, Eric Youngdale, Fergus Henderson, H.J. Lu, Jens Schweikhardt, Kai Petzke, Michael Meissner, Mitchum DSouza, Olaf Flebbe, Paul Gortmaker, Rik Faith, Steven S. Dick, Tuomas J Lukka, y por supuesto Linux Torvalds, sin el cual todo esto habria sido imposible.

No te sientas ofendido si tu nombre no aparece aqui y tu has contribuido a este documento (como HOWTO o como FAQ). Mandame un e-mail y rectificare.

9.2 Traducciones

En este momento, no hay ninguna traduccion conocida de este trabajo (N.T. Ahora ya hay al menos una). Si deseas traducirlo, hazlo, pero por favor dimelo. Hay pocas posibilidades de que hable el lenguaje al que quieres traducirlo, pero te ayudare en todo lo que pueda.

9.3 Correspondencia

Mandame el correo a dan@detached.demon.co.uk. Mi clave publica PGP (ID 5F263625) esta disponible en mis paginas web http://ftp.linux.org.uk/~barlow/, si sientes la necesidad de ser secreto con lo que cuentas.

9.4 Apartado Legal

Todas las marcas registradas usadas en este documento pertenecen a sus respectivos dueños. El copyright de este documento es de Daniel Barlow (C) 1996 <dan@detached.demon.co.uk> Puede ser reproducido y distribuido enteramente o en parte, en cualquier medio fisico o electronico, siempre que la informacion de copyright se incluya en todas las copias. La redistribucion comercial esta permitida; de cualquier manera, al autor le gustaria ser informado de dicha distribucion.

Todas las traducciones, trabajos basados u otros trabajos que incorporen cualquier documento Linux HOWTO deben incluir esta informacion de copyright. No puedes hacer un trabajo basado en un HOWTO e imponer restricciones adicionales a su distribucion. Pueden hacerse excepciones a estas reglas bajo ciertas condiciones; contacta con el coordinador de los Linux HOWTO en la direccion que se da abajo.

Deseamos promover la diseminacion de esta informacion a traves de tantos canales como sea posible. A pesar de esto, queremos retener el copyright en los documentos HOWTO, y nos gustaria que nos informaran de cualquier plan de distribucion de los HOWTOs.

Si tienes alguna cuestion, contacta por favor con Greg Hankins, el coordinador de los Linux HOWTO, en gregh@sunsite.unc.edu mediante email.

10. Indice

Las entradas que empiezan con un caracter no alfabetico estan listadas en orden ASCII.