GCC COMO <author><tt><htmlurl url="mailto:dan@detached.demon.co.uk" name="dan@detached.demon.co.uk"> &nl; Traducido por Borja Muñoz Aguilar, <tt><htmlurl url="mailto:x4354281@turing.ugr.es" name="x4354281@turing.ugr.es"> </tt>) <date>v1.17, Traducción del 21 de Mayo de 1998. <abstract> 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. </abstract> <sect> Preliminares <sect1> ELF vs. a.out <label id="index.0"> <!-- elf --> <label id="index.1"> <!-- a.out --> <p> 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. <label id="index.2"> <!-- <tt/file/ --> ¿Cómo saberlo? Use la orden <tt/file/ (p.ej. <tt>file /bin/bash</tt>). 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. <sect1> Administrata <label id="index.3"> <!-- copyright --> <p>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. <sect1> Tipografía <p>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 <tt/typewriter/, mientras que las 'variables' que necesitan ser enfatizadas lo estan asi: <em/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 <tscreen><verb> % setenv FOO bar </verb></tscreen> donde yo habria escrito <tscreen><verb> $ FOO=bar; export FOO </verb></tscreen> Si el prompt es <tt/#/ en vez de <tt/$/, 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 <tt/:-)/ <sect>Dónde obtener lo necesario <p> <sect1>Este documento <p> Este documento es uno de la serie Linux HOWTO, asi que está disponible en todos los sitios que mantienen Linux HOWTOs como <tt><htmlurl url="http://sunsite.unc.edu/pub/linux/docs/HOWTO/" name="http://sunsite.unc.edu/pub/linux/docs/HOWTO/"></tt><footnote>O <tt><htmlurl url="ftp://ftp.insflug.org" name="ftp.insflug.org"></tt> para las traducciones en Castellano</footnote> La versión HTML puede ser encontrada (posiblemente una versión más reciente) en <tt><htmlurl url="http://ftp.linux.org.uk/~barlow/howto/gcc-howto.html" name="http://ftp.linux.org.uk/~barlow/howto/gcc-howto.html"></tt>. <sect1> Otra documentación <label id="index.4"> <!-- documentation --> <p> La documentación oficial para gcc está en la distribución fuente (ver abajo) como ficheros texinfo, y como ficheros <tt/.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 <tt>/usr/info</tt>. Si no, puedes encontrarlo en <tt><htmlurl url= "ftp://tsx-11.mit.edu/pub/linux/packages/GCC/" name="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/"></tt>, pero no necesariamente siempre la última versión. <label id="index.5"> <!-- manual pages --> <p>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 <tt><htmlurl name="ftp://sunsite.unc.edu/pub/Linux/docs/" url="ftp://sunsite.unc.edu/pub/Linux/docs/"></tt> están escritas para Linux y describen un montón de llamadas al sistema (sección 2) y funciones de libc (sección 3). <sect1>GCC <label id="index.6"> <!-- gcc --> <p> Hay dos respuestas. (a) La distribución oficial de GCC para Linux puede ser encontrada siempre en formato binario (ya compilada) en <url url= "ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/">. En el momento de escribir esto, la última versión es la 2.7.2 (<tt/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 <url name="GNU archives" url= "ftp://tsx-11.mit.edu:/pub/gnu/">. 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 <url name="tsx-11" url= "ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/">. Para compilar las cosas que no sean triviales (y también para algunas triviales) necesitarás también: <sect1> La librería de C y los ficheros de cabecera <label id="index.7"> <!-- libc --> <p>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 <url name="tsx-11" url= "ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/"> como arriba: <descrip> <tag/<tt/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. <tag/<tt/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. <tag/<tt/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. </descrip> <sect1> Herramientas asociadas (as, ld, ar, strings etc) <label id="index.8"> <!-- <tt/as/ --> <label id="index.9"> <!-- <tt/ld/ --> <label id="index.10"> <!-- <tt/ar/ --> <label id="index.11"> <!-- <tt/strings/ --> <p> Desde <url name = "tsx-11" url= "ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/">, como todo lo anterior. La versión actual es <tt/binutils-2.6.0.2.bin.tar.gz/. <p> 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. <sect> Instalacion y Configuracion de GCC <sect1> Versiones de GCC <label id="index.12"> <!-- version numbers --> <label id="index.13"> <!-- gcc, flags --> <label id="index.14"> <!-- gcc -v --> <p>Puedes saber la version de GCC que estas ejecutando tecleando <tt>gcc -v</tt> 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: <tscreen><verb> $ gcc -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 </verb></tscreen> <p> Podemos sacar la siguiente informacion: <itemize> <item> <tt/i486/. Indica que el GCC que estás usando fue creado para un procesador 486 -- aunque tu tengas un 386 o un 586. Todos estos chips pueden ejecutar codigo compilado para cada uno de los otros; la diferencia es que el codigo del 486 esta optimizado en algunas partes con lo que se ejecuta mas rapido en un 486. No provoca un peor rendimiento en el 386 sino que hace los binarios un poco más grandes. <item> <tt/box/. <em/No/ es importante en absoluto, y puede poner algo más (como <tt/slackware/ o <tt/debian/) o nada en absoluto (asi que el nombre completo del directorio es <tt/i486-linux/). Si compilas tu propio gcc, puedes configurarlo en tiempo de compilación para un efecto ornamental. Como yo hice <tt/:-)/ <item> <tt/linux/. Puede ser <tt/linuxelf/ o <tt/linuxaout/, y, el significado de cada uno de ellos varia segun la version que estes usando. <itemize> <item> <tt/linux/ significa ELF si la version es la 2.7.0 o mas reciente, a.out en otro caso. <item><tt/linuxaout/ quiere decir a.out. Fue introducido cuando la definicion de <tt/linux/ se cambio de a.out a ELF, asi que no veras ningun <tt/linuxaout/ gcc mas antiguo que el 2.7.0. <label id="index.15"> <!-- gcc, bugs --> <item> <tt/linuxelf/ esta obsoleto. Es generalmente una version del gcc 2.6.3 para producir ejecutables ELF. La version 2.6.3 tiene fallos detectados cuando produce codigo para ELF -- es recomendable una actualizacion. </itemize> <item> <tt/2.7.2/ es el numero de version. </itemize> Asi que, en resumen, tengo el gcc 2.7.2 produciendo codigo ELF. Vaya sorpresa. <sect1> ¿Donde está? <p>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: <itemize> <item> <tt>/usr/lib/gcc-lib/</tt><em>target</em><tt>/</tt><em/version/<tt>/</tt> (y subdirectorios) es probablemente el lugar en el que esta el compilador. Incluye los programas ejecutables que compilan, y algunas versiones especificas de librerias y ficheros include. <item> <tt>/usr/bin/gcc</tt> es lo que tu ejecutas desde la linea de comandos. Puede ser usado con multiples versiones de gcc si tienes multiples directorios para el compilador instalados. Para encontrar la version por defecto que usa, escribe <tt/gcc -v/. Para forzarlo a usar otra version, escribe <tt/gcc -V/ <em/version/. Por ejemplo <tscreen><verb> # gcc -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 # gcc -V 2.6.3 -v Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.6.3/specs gcc driver version 2.7.2 executing gcc version 2.6.3 </verb></tscreen> <item> <tt>/usr/</tt><em>target</em><tt>/(bin|lib|include)/</tt>. Si tienes instalado soporte para distintos ejecutables (por ejemplo, a.out y elf), o un compilador cruzado, las librerias, binutils (<tt/as/, <tt/ld/ y otras) y los ficheros de cabecera para los ejecutables no nativos pueden ser encontrados aqui. Incluso si tu solo tienes un tipo de gcc instalado puedes encontrar que algunas de sus partes se encuentran. Si no, estan en <tt>/usr/(bin|lin|include)</tt>. <item> <tt>/lib/</tt>,<tt>/usr/lib</tt> y otros son los directorios en los que estan las librerias para el sistema nativo. Tambien necesitaras <tt>/lib/cpp</tt> para muchas aplicaciones (X hace bastante uso de el) -- o copialo de <tt>/usr/lib/gcc-lib/</tt><em>target</em><tt>/</tt><em/version/<tt>/</tt> o haz que un enlace simbolico apunte aqui. <label id="index.16"> <!-- /lib/cpp --> </itemize> <sect1> ¿Donde estan los ficheros de cabecera? <label id="index.17"> <!-- header files --> <p>Aparte de lo que tu instales bajo <tt>/usr/local/include</tt>, hay tres fuentes principales de ficheros de cabecera en Linux. <itemize> <item> La mayoria del <tt>/usr/include/</tt> y sus subdirectorios tienen el paquete binario libc de H J Lu. Digo la mayoria porque tambien puedes tener ficheros de otras fuentes (librerias <tt/dbm/, por ejemplo) aqui, especialmente si estas usando la distribucion libc mas reciente (que no viene con dbm, como las antiguas). <label id="index.18"> <!-- <linux/*.h> --> <label id="index.19"> <!-- <asm/*.h> --> <item> <tt>/usr/include/linux</tt> y <tt>/usr/include/asm</tt> (para los ficheros <tt><linux/*.h></tt> y <tt><asm/*.h></tt>) deberian ser enlaces simbolicos a los directorios <tt>linux/include/linux</tt> y <tt>linux/include/asm</tt> en la distribucion fuente del kernel. Necesitas instalarlos si planeas desarrollar cualquier cosa no trivial; no estan solo ahi para compilar el kernel. Puedes encontrarte tambien con que necesitas hacer <tt>make config</tt> en el directorio del kernel despues de descomprimir los fuentes. Muchos ficheros dependen de <tt><linux/autoconf.h></tt> que de otra manera no existiria, y en algunas versiones recentes del kernel <tt/asm/ es un enlace simbolico a el mismo y solo se crea cuando se hace el <tt/make config/. Asi que si descomprimes los fuentes del kernel en /usr/src/linux, <tscreen><verb> $ cd /usr/src/linux $ su # make config [Responde a las preguntas. A menos de que sigas y compiles el kernel no importa demasiado lo que digas] # cd /usr/include # ln -s ../src/linux/include/linux . # ln -s ../src/linux/include/asm . </verb></tscreen> <label id="index.20"> <!-- <float.h> --> <label id="index.21"> <!-- <limits.h> --> <label id="index.22"> <!-- <varargs.h> --> <label id="index.23"> <!-- <stdarg.h> --> <label id="index.24"> <!-- <stddef.h> --> <item> Ficheros como <tt/<float.h>/, <tt/<limits.h>/, <tt/<varargs.h>/, <tt/<stdarg.h>/ y <tt/<stddef.h>/ varian con la version del compilador, asi que se encuentran en <tt>/usr/lib/gcc-lib/i486-box-linux/2.7.2/include/</tt> o similar. </itemize> <sect1> Construyendo compiladores cruzados <sect2> Linux como plataforma de ejecutables <p> Suponiendo que tienes el codigo fuente para gcc, normalmente puedes seguir las instrucciones que se dan en el fichero INSTALL para GCC. Un <tt/configure --target=i486-linux --host=XXX/ en la plataforma <tt/XXX/ seguido de un <tt/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 <tt><htmlurl url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/" name="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/"></tt>. <sect2> Linux como plataforma fuente, MSDOS como plataforma de ejecutables <p>Uff. Aparentemente esto es posible si se utiliza el paquete "emx" o el extensor "go". Mira en: <tt><htmlurl url="ftp://sunsite.unc.edu/pub/Linux/devel/msdos" name="ftp://sunsite.unc.edu/pub/Linux/devel/msdos"></tt>. No lo he probado, asi que no puedo decir nada mas. <sect> Portando codigo y compilando. <sect1> Simbolos automaticamente definidos <label id="index.25"> <!-- gcc, flags --> <p>Puedes saber que simbolos define tu version de gcc automaticamente ejecutandolo con la opcion <tt/-v/. Por ejemplo, la mia: <tscreen><verb> $ 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__ - </verb></tscreen> Si estas escribiendo codigo que usa caracteristicas especificas de Linux es buena idea meter la parte no portable entre: <tscreen><verb> #ifdef __linux__ /* ... Lo que sea ... */ #endif /* linux */ </verb></tscreen> Usa <tt>__linux__</tt> para esto, <em/nunca/ <tt/linux/. Aunque esta definido no cumple las normas POSIX. <sect1> Invocacion del compilador <p> La documentacion sobre las opciones del compilador es la pagina info de gcc (en Emacs, usa <tt>C-h i</tt> 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 <tt><htmlurl url="ftp://prep.ai.mit.edu/pub/gnu" name="ftp://prep.ai.mit.edu/pub/gnu"></tt> o uno de sus mirrors, y copiarla de ahi. La pagina del manual de gcc (<tt/gcc.1/), esta desfasada. Te avisara de esto cuando quieras consultarla. <sect2> Opciones del compilador <label id="index.26"> <!-- gcc, flags --> <label id="index.27"> <!-- optimisation --> <p>gcc puede optimizar el codigo que genera si añadimos <tt/-O/<em/n/ a su linea de comandos, donde <em/n/ es un entero pequeño opcional. Los valores de <em/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 <tt/-f/ y <tt/-m/. Puedes ver exactamente cuales niveles <tt/-O/ se corresponden con que opciones ejecutando gcc con la opcion <tt/-v/ y la opcion (indocumentada) <tt/-Q/. Por ejemplo, para <tt/-O2/ el mio dice: <tscreen><verb> 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 </verb></tscreen> Si utilizas un nivel de optimizacion mayor que el que soporta tu compilador (p.ej. <tt/-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. <label id="index.28"> <!-- gcc, bugs --> Los usuarios de gcc 2.7.0 hasta 2.7.2 se daran cuenta de que hay un error en <tt/-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 <tt/-fno-strength-reduce/. <sect3> Cuestiones especificas de procesadores <p> Hay otras opciones <tt/-m/ que no se activan con <tt/-O/ pero que son utiles. Las mas importantes son <tt/-m386/ y <tt/-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 <tt/-mpentium/ o <tt/-m586/. Linus sugiere usar <tt/-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: <quote> <tt/-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 <tt/-fcaller-saves/ puede ser una perdida. </quote> <quote> <tt/-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, <tt/-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. </quote> Los ultimos comentarios en este punto son tambien de Linus: <quote> 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. </quote> <sect2> <tt/Internal compiler error: cc1 got fatal signal 11/ <label id="index.29"> <!-- gcc, bugs --> <label id="index.30"> <!-- segmentation fault --> <label id="index.31"> <!-- SIGSEGV --> <label id="index.32"> <!-- SIGSEGV, in gcc --> <label id="index.33"> <!-- segmentation fault, in GCC --> <p> 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). <bf/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 '<tt/make zImage/' -- ¡por supuesto lo hara! '<tt/make zImage/' esta probablemente compilando mas de 200 ficheros. <p>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. <sect1> Portabilidad <p>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. <sect2> Cuestiones BSD (incluyendo <tt/bsd_ioctl/, <tt/daemon/ y <tt/<sgtty.h>/) <p>Puedes compilar tu programa con <tt>-I/usr/include/bsd</tt> y linkarlo con <tt>-lbsd</tt> (p.ej. añade <tt>-I/usr/include/bsd</tt> a <tt>CFLAGS</tt> y <tt>-lbsd</tt> a <tt>LDFLAGS</tt> en tu Makefile). <em/No/ hay necesidad de añadir <tt>-D__USE_BSD_SIGNAL</tt> si quieres un comportamiento con señales tipo BSD, porque lo obtienes automaticamente cuando pones <tt>_I/usr/include/bsd</tt> e incluyes <tt/<signal.h>/. <sect2> Señales perdidas (SIGBUS, SIGEMT, SIGIOT, SIGTRAP, SIGSYS etc) (<tt/SIGBUS/, <tt/SIGEMT/, <tt/SIGIOT/, <tt/SIGTRAP/, <tt/SIGSYS/ etc) <label id="index.34"> <!-- <tt/SIGBUS/ --> <label id="index.35"> <!-- <tt/SIGEMT/ --> <label id="index.36"> <!-- <tt/SIGIOT/ --> <label id="index.37"> <!-- <tt/SIGTRAP/ --> <label id="index.38"> <!-- <tt/SIGSYS/ --> <p>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: <quote> ``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.'' </quote> <p>El modo mas facil para solucionarlo es redefinir estas señales a <tt/SIGUNUSED/. El modo <em/correcto/ es meter el codigo que las maneja entre <tt/#ifdef/s apropiados: <tscreen><verb> #ifdef SIGSYS /* ... el codigo no POSIX para SIGSYS va aqui .... */ #endif </verb></tscreen> <sect2> Codigo K & R <label id="index.39"> <!-- <tt/-fwritable-strings/ --> <p>GCC es un compilador ANSI; pero bastante codigo existente no es ANSI. No se puede hacer mucho con respecto a esto, excepto añadir <tt/-traditional/ a las opciones del compilador. Hay una cierta cantidad de controles acerca de que posibilidades emular; consulta la pagina info de gcc. <tt/-traditional/ tiene otros efectos ademas de cambiar el lenguaje que gcc reconoce. Por ejemplo, cambia a <tt/-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. <sect2> Los simbolos del preprocesador provocan conflictos con los prototipos en el codigo <label id="index.40"> <!-- <tt/atoi()/ --> <label id="index.41"> <!-- <tt/atol()/ --> <p> 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 <tt/atoi()/ y <tt/atol()/. <sect2> <tt>sprintf()</tt> <label id="index.42"> <!-- <tt/sprintf()/ --> Algo de lo que hay que estar avisado, especialmente cuando se porta desde SunOS, es que <tt/sprintf(string, fmt, ...)/ devuelve un puntero a <tt/cadena/ en varias unidades, mientras que Linux (siguiendo las normas ANSI) devuelve el numero de caracteres que fueron puestos en el string. <sect2> ¿ <tt/FD_*/ ? <tt/fcntl/ y amigos. ¿ Donde estan las definiciones ? <tt/FD_*/ stuff ? <label id="index.43"> <!-- <tt/FD_SET/ --> <label id="index.44"> <!-- <tt/FD_CLR/ --> <label id="index.45"> <!-- <tt/FD_ISSET/ --> <label id="index.46"> <!-- <tt/FD_ZERO/ --> <label id="index.47"> <!-- <tt/fcntl/ --> <label id="index.48"> <!-- <tt><sys/time.h></tt> --> <label id="index.49"> <!-- <tt><unistd.h></tt> --> <p> En <tt><sys/time.h></tt>. Si estas usando <tt/fcntl/ probablemente querras incluir <tt/<unistd.h>/ tambien, para el prototipo. En general, la pagina del manual para una funcion lista los <tt/#includes/ necesarios en su seccion SYNOPSIS. <sect2> El <tt>select()</tt> timeout. Programas que empiezan en espera ocupada. <label id="index.50"> <!-- select() --> <p>Hubo un tiempo en el que el parametro timeout de <tt>select()</tt> se usaba solo para lectura. Incluso entonces la pagina del manual avisaba: <quote> 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(). </quote> ¡El futuro ha llegado! Al menos, esta aqui. Como vuelta de un <tt/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. <p>Para solucionarlo pon el valor de timeout en esa estructura cada vez que llames a <tt/select()/. Cambia el codigo <tscreen><verb> struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; while (some_condition) select(n,readfds,writefds,exceptfds,&timeout); </verb></tscreen> por, <tscreen><verb> struct timeval timeout; while (some_condition) { timeout.tv_sec = 1; timeout.tv_usec = 0; select(n,readfds,writefds,exceptfds,&timeout); } </verb></tscreen> 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. <sect2> Llamadas al sistema interrumpidas. <label id="index.51"> <!-- interrupted system calls --> <label id="index.52"> <!-- EINTR --> <sect3> Síntoma: <p>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. <sect3> Problema: <p>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: <itemize> <item> Asincronamente (con cada tick del temporizador) <item> Como vuelta de cualquier llamada al sistema <item> Durante la ejecucion de las siguientes llamadas al sistema: <tt>select()</tt>, <tt>pause()</tt>, <tt>connect()</tt>, <tt>accept()</tt>, <tt>read()</tt> en terminales, sockets, pipes o ficheros en <tt>/proc</tt>, <tt>write()</tt> en terminales, sockets, pipes o la impresora, <tt>open()</tt> on FIFOs, PTYs or serial lines, <tt>ioctl()</tt> on terminales, <tt>fcntl()</tt> con el comando <tt/F_SETLKW/, <tt>wait4()</tt>, <tt>syslog()</tt>, y cualquier operación TCP o NFS. </itemize> Para otros sistemas operativos puede que tengas que incluir las llamadas al sistema <tt>creat()</tt>, <tt>close()</tt>, <tt>getmsg()</tt>, <tt>putmsg()</tt>, <tt>msgrcv()</tt>, <tt>msgsnd()</tt>, <tt>recv()</tt>, <tt>send()</tt>, <tt>wait()</tt>, <tt>waitpid()</tt>, <tt>wait3()</tt>, <tt>tcdrain()</tt>, <tt>sigpause()</tt>, <tt>semop()</tt>creat(), a esa lista. <p>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 <tt/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 <tt/SA_RESTART/ a los flags de sigaction. Por ejemplo, cambia: <tscreen><verb> signal (sig_nr, my_signal_handler); </verb></tscreen> a <tscreen><verb> signal (sig_nr, my_signal_handler); { struct sigaction sa; sigaction (sig_nr, (struct sigaction *)0, &ero;sa); #ifdef SA_RESTART sa.sa_flags |= SA_RESTART; #endif #ifdef SA_INTERRUPT sa.sa_flags &ero;= ~ SA_INTERRUPT; #endif sigaction (sig_nr, &ero;sa, (struct sigaction *)0); } </verb></tscreen> A pesar de que esto se aplica a la mayoria de llamadas al sistema, debes chequear si se devuelve <tt/EINTR/ en <tt/read()/, <tt/write()/, <tt/ioctl()/, <tt/select()/, <tt/pause()/ y <tt/connect()/. Mira abajo. (2) Chequear <tt/EINTR/ explicitamente: Aqui hay dos ejemplos para <tt/read()/ e <tt/ioctl()/, Pieza original de codigo usando <tt/read()/ <tscreen><verb> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) break; buffer += result; len -= result; } </verb></tscreen> se convierte en <tscreen><verb> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) { if (errno != EINTR) break; } else { buffer += result; len -= result; } } </verb></tscreen> y un fragmento de codigo que usa <tt/ioctl()/ <tscreen><verb> int result; result = ioctl(fd,cmd,addr); </verb></tscreen> se convierte en <tscreen><verb> int result; do { result = ioctl(fd,cmd,addr); } while ((result == -1) && (errno == EINTR)); </verb></tscreen> 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 <tt/SV_INTERRUPT/ o <tt/SA_INTERRUPT/. <sect2> Cadenas escribibles (el segmento del programa falla aleatoriamente) <label id="index.53"> <!-- SIGSEGV --> <label id="index.54"> <!-- segmentation fault --> <label id="index.55"> <!-- <tt/mktemp()/ --> <label id="index.56"> <!-- <tt/-fwritable-strings/ --> <p>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 <tt>mktemp()</tt> con una constante de cadena como argumento. <tt/mktemp()/ intenta reescribir su argumento. Para solucionarlo, una de dos: (a) compila con <tt/-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. <sect2> ¿Porque falla la llamada a <tt>execl()</tt>? <label id="index.57"> <!-- <tt/execl()/ --> <p> Porque no la llamas bien. El primer argumento de <tt/execl/ es el programa que quieres ejecutar. El segundo y los siguientes son el array <tt/argv[]/ del programa que estas llamando. Recuerda: <tt/argv[0]/ siempre contiene el nombre del programa incluso cuando se ejecuta sin argumentos. Asi que deberias escribir <tscreen><verb> execl("/bin/ls", "ls", NULL); </verb></tscreen> no solo <tscreen><verb> execl("/bin/ls", NULL); </verb></tscreen> <p> 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 <tt/ldd/). <sect> Depurando y perfilando. <sect1> Mantenimiento preventivo (lint) <label id="index.58"> <!-- lint --> No hay ningun lint que sea ampliamente usado para Linux, ya que la mayoria de la gente esta satisfecha con los warnings que genera gcc. Probablemente el mas util es el switch <tt/-Wall/ -- que quiere decir "Warnings, all (Todos los Warnings)". Hay un lint de dominio publico disponible en <tt><htmlurl url="ftp://larch.lcs.mit.edu/pub/Larch/lclint" name="ftp://larch.lcs.mit.edu/pub/Larch/lclint"></tt>. No se como de bueno es. <sect1> Depuracion. <label id="index.59"> <!-- debugging --> <sect2> ¿Como introduzco la informacion de depuracion en un programa? <label id="index.60"> <!-- <tt/gcc -g/ --> <label id="index.61"> <!-- <tt/gcc -fomit-frame-pointer/ --> <label id="index.62"> <!-- <tt/libg.a/ --> <p> Necesitas compilar y enlazar todas sus partes con la opcion <tt/-g/, y sin la opcion <tt/-fomit-frame-pointer/. No necesitas recompilarlo entero, solo las partes que estes interesado en depurar. <p> En configuraciones a.out las librerias compartidas estan compiladas con <tt/-fomit-frame-pointer/, asi que gdb no trabajara con ellas. Si le ponemos la opcion <tt/-g/ cuando linkamos deberia implicar un linkado estatico, esa es la explicacion. <p> Si el linkador falla y devuelve un mensaje diciendo que no encuentra libg.a, no tienes <tt>/usr/lib/libg.a</tt> 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 <tt>/usr/lib/libc.a</tt> <sect3> ¿Como la saco ahora? <label id="index.63"> <!-- binaries too big --> <p> Mucho software GNU viene preparado para compilar y linkar con <tt/-g/, provocando ejecutables muy grandes. No es una gran idea. <p>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 <tt/"destriparlo"/. <sect2> Software disponible <label id="index.64"> <!-- gdb --> <p> La mayoria de la gente usa <bf/gdb/, puedes obtener los fuentes de <url url="ftp://prep.ai.mit.edu/pub/gnu" name="Archivos GNU">, o los binarios de <url url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC" name="tsx-11"> o sunsite. <bf/xxgdb/ es un depurador X basado en el anterior (necesitas tener instalado gdb). El codigo fuente puede encontrarse en <tt><htmlurl url="ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz" name="ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz"></tt>. Tambien, el depurador <bf/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 <tt><htmlurl url="ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/" name="ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/"></tt>, y el codigo original en <tt><htmlurl url="ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z" name="ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z"></tt>. Otra herramienta que te puede ser util para depurar es '<bf/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 <tt><htmlurl url="ftp://ftp.std.com/pub/jrs/" name="ftp://ftp.std.com/pub/jrs/"></tt>. <sect2> Programas que se ejecutan en segundo plano (demonios). <p> Los demonios tipicamente ejecutan <tt/fork()/ tempranamente, y terminan el padre. Se hace esto para una corta sesion de depuracion. <p>La manera mas simple de atajar esto es poner un punto de ruptura para <tt/fork/, y cuando el programa pare, forzarlo a devolver cero. <tscreen><verb> (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 } </verb></tscreen> <sect2> Ficheros core. <p>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: <tscreen><verb> % limit core unlimited </verb></tscreen> mientras que para los shell como el Bourne (sh, bash, zsh, pdksh) se usa: <tscreen><verb> $ ulimit -c unlimited </verb></tscreen> 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 <tt/fs/binfmt_aout.c/ y <tt/fs/binfmt_elf.c/ (en los kernels mas recientes, tienes que hacer un grep) que dice: <tscreen><verb> memcpy(corefile,"core.",5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif </verb></tscreen> y cambia los ceros por unos. <sect1> 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 <tt/-p/, y necesitaras tambien <tt/gprof/ (del paquete binutils). Mira la pagina del manual para <tt/gprof/ para los detalles. <sect> Enlazando. <p> 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. <p> 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. <sect1> Librerias compartidas vs librerias estaticas. <p> 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 <tt>/lib</tt> y en <tt>/usr/lib</tt>, ademas de en otros sitios. <label id="index.65"> <!-- binaries too big --> <label id="index.66"> <!-- statically linked binaries, unexpected --> 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 (<tt/*.sa/ para a.out, <tt/*.so/ para ELF) estan donde deberian esta, y tienen permiso de lectura. En Linux, las librerias estaticas tienen nombres como <tt/nombre_de_libreria.a/, mientras que las compartidas se llaman <tt/nombre_de_libreria.so.x.y.z/ donde <tt/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 <tt/.sa/ asociados. Las librerias estandar vienen en los dos formatos, compartidas y estaticas. Puedes saber que librerias compartidas requiere un programa usando <tt/ldd/ (Listar Dependencias Dinamicas) <tscreen><verb> $ ldd /usr/bin/lynx libncurses.so.1 => /usr/lib/libncurses.so.1.9.6 libc.so.5 => /lib/libc.so.5.2.18 </verb></tscreen> En mi sistema el browser para WWW 'linx' depende de la presencia de <tt/libc.so.5/ (la libreria C) y <tt/libncurses.so.1/ (usado para control de terminales). Si un programa no tiene dependencias, <tt/ldd/ dira "<tt/statically linked/ (enlazado estaticamente)" o "<tt/statically linked (ELF)/". <sect1> Preguntando a las librerias ("¿En que libreria esta <tt/sin()/?") <label id="index.67"> <!-- <tt/sin()/ --> <label id="index.68"> <!-- <tt/cos()/ --> <label id="index.69"> <!-- maths --> <p> <tt/nm/ <em/nombre_de_libreria/ deberia listar todos los simbolos a los que ese <em/nombre_de_libreria/ tiene referencias. Trabaja tanto con librerias estaticas como con compartidas. Supon que quieres saber donde esta definidad <tt/tcgetattr()/, puedes hacer: <tscreen><verb> $ nm libncurses.so.1 |grep tcget U tcgetattr </verb></tscreen> La <tt/U/ quiere decir "undefined (no definido)" --- quiere decir que la libreri ncurses la usa pero no la define. Tambien puedes hacer: <tscreen><verb> $ nm libc.so.5 | grep tcget 00010fe8 T __tcgetattr 00010fe8 W tcgetattr 00068718 T tcgetpgrp </verb></tscreen> La <tt/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 <tt/tcgetpgrp/) esta marcada con una <tt/T/. <label id="index.70"> <!-- <tt/<math.h>/ --> La respuesta para la pregunta del titulo, es <tt/libm.(so|a)/. Todas las funciones definidas en <tt><math.h></tt> se guardan en la libreria de matematicas; aunque necesitaras enlazarlas con <tt/-lm/ cuando uses alguna. <sect1> Encontrando ficheros <p><tt/ld: Output file requires shared library 'libfoo.so.1'/ (ld : fichero de salida requiere libreria compartida 'libfoo.so.1') <p> 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 <tt>/usr/lib</tt>. Si quieres que busquen librerias en otros lugares, especifica sus directorios con la opcion <tt/-L/ en gcc o ld. <p> Si eso no ayuda, chequea que tengas el fichero correcto en ese lugar. Para a.out, enlazar con <tt/-lfoo/ provoca que ld busque <tt/libfoo.sa/ (la compartida), y si no tiene exito <tt/libfoo.a/ (la estatica). Para ELF, busca <tt/libfoo.so/ y entonces <tt/libfoo.a/. <tt/libfoo.so/ es normalmente un enlace simbolico a <tt/libfoo.so.x/. <sect1> Construyendo tus propias librerias <sect2> Control de versiones <p> 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: <tt/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 <tt/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. <sect2> ¿ELF? ¿Que es? <label id="index.71"> <!-- elf --> <p> 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. <sect3> ¿Otra vez? <p> Esta seccion se ha cogido del documento '/news-archives/comp.sys.sun.misc'. <quote> 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. </quote> <quote> 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). </quote> 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. <sect3> Librerías compartidas de ELF <p> Para construir <tt/libfoo.so/ como una libreria compartida, los pasos basicos son estos: <tscreen><verb> $ 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 </verb></tscreen> Esto generara una libreria compartida llamada <tt>libfoo.so.1.0</tt>, y los enlaces apropiados para ld (<tt>libfoo.so</tt>) y el cargador dinamico (<tt>libfoo.so.1</tt>) para que puedan encontrarla. Para testearlo, añadimos el directorio actual al <tt/LD_LIBRARY_PATH/. <label id="index.72"> <!-- weird things --> Cuando veas que la libreria funciona, tendras que moverla a, digamos, <tt>/usr/local/lib</tt>, y volver a crear los links apropiados. El enlace de <tt/libfoo.so.1/ a <tt/libfoo.so.1.0/ es mantenido por <tt/ldconfig/, que se ejecuta en la mayoria de los sistemas como una parte del proceso de arranque. El enlace a <tt/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 <tt/libfoo.so/ a <tt/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. <tscreen><verb> $ su # cp libfoo.so.1.0 /usr/local/lib # /sbin/ldconfig # ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so ) </verb></tscreen> <sect3> Numeracion de versiones, sonames y enlaces simbolicos. <label id="index.73"> <!-- soname --> <label id="index.74"> <!-- version numbers --> <p> Cada libreria tiene un <em/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 <tt/libfoo.so/ podria tener como soname <tt/libbar.so/, y todos los programas que se enlacen a ella buscaran <tt/libbar.so/ cuando empiecen. <p> 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, <tt/libfoo.so.1.2/, y darle un soname como <tt/libfoo.so.1/. Si se añade a un directorio estandar de librerias (p.ej. <tt>/usr/lib</tt>), <tt/ldconfig/ creara un enlace simbolico: <tt>libfoo.so.1 -> libfoo.so.1.2</tt> para que la imagen apropiada se encuentre en tiempo de ejecucion. Tambien necesitaras crear un enlace <tt>libfoo.so -> libfoo.so.1</tt> para que ld encuentre el soname correcto a utilizar en tiempo de ejecucion. <p> 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 <tt/libfoo.so.2.0/, y le damos el soname <tt/libfoo.so.2/. Ahora cambiamos el enlace de <tt/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 <tscreen><verb> gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor </verb></tscreen> y todo estara correcto. <sect2> a.out. El formato tradicional. <p> La facilidad de construir librerias compartidas es una de las mayores razones para actualizarse a ELF. Aunque es posible hacerlo en a.out. Consigue <tt><htmlurl url="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/src/tools-2.17.tar.gz" name="ftp://tsx-11.mit.edu/pub/linux/packages/GCC/src/tools-2.17.tar.gz"></tt> y lee el documento de 20 paginas que encontrarlas despues de descomprimirlo. <sect3> ZMAGIC vs QMAGIC <label id="index.75"> <!-- ZMAGIC --> <label id="index.76"> <!-- QMAGIC --> <p> 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. <sect3> Colocacion de ficheros <p> 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 <tt/libfoo.sa/ y <tt/libfoo.so.1.2;/ el enlace simbolico seria <tt/libfoo.so.1/ y apuntaria al mas reciente de los archivos. ¿Para que son? En tiempo de compilacion, <tt/ld/ busca <tt/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 <tt/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, <tt/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 <tt/cp/ o usar el programa <tt/makehole/ lograra esto. Tambien puedes destriparlas despues de construirlas, ya que las direcciones estan en posiciones fijas. <bf>No intentes destripar las librerias ELF</bf>. <sect3> ¿"libc-lite"? <p> 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 <tt>/lib/libc.so.4</tt> esta enlazada a una libreria lite, deberias reemplazarla con una version completa. <sect2> Enlazado : problemas comunes <p>!Mandame tus problemas de enlazado! Probablemente no los solucionare pero los escribire si tengo bastante... <descrip> <tag> Los programas se enlazan estaticamente a pesar de que tu los quieres compartidos </tag> <label id="index.77"> <!-- binaries too big --> <label id="index.78"> <!-- statically linked binaries, unexpected --> Chequea que tienes los enlaces correctos para que <tt/ld/ encuentre cada libreria compartida. Para ELF esto significa que <tt/libfoo.so/ esta enlazado simbolicamente a la imagen, para a.out un fichero <tt/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. <tag> La herramienta DLL 'mkimage' no encuentra libgcc </tag> <label id="index.79"> <!-- libgcc --> A partir de <tt/libc.so.4.5.x/ libgcc deja de ser compartido. Asi que debes reemplazar todas las ocurrencias de '<tt/-lgcc/' con '<tt/gcc -print-libgcc-file-name'/. Tambien, borra todos los archivos <tt>/usr/lib/libgcc*</tt>. Esto es importante. <tag> Multiples mensajes con <tt>__NEEDS_SHRLIB_libc_4</tt> </tag> son otra consecuencia del mismo problema. <tag> ¿ Se visualiza el mensaje "Assertion failure" cuando reconstruyes una DLL? </tag> 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 <tt/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. <tag> <tt/ld: output file needs shared library libc.so.4/ (ld: fichero de salida necesita libreria compartida libc.so.4) </tag> Esto ocurre usualmente cuando estas enlazando con otras librerias que no son libc (p.ej. librerias X) y usas la opcion <tt/-g/ al enlazar sin usar <tt/-static/. Los stubs <tt/.sa/ para las librerias compartidas tienen usualmente un simbolo no definido <tt/_NEEDS_SHRLIB_libc_4/ que es resuelto por el stub <tt/libc.sa/. De todos modos con <tt/-g/ acabas enlazando con <tt/libg.a/ o <tt/libc.a/ y no se resuelva nunca este simbolo, haciendo que se imprime el mensaje de error anterior. En conclusion, añade <tt/-static/ cuando compiles con la opcion <tt/-g/, o no enlaces con <tt/-g/. A menudo puedes obtener suficiente informacion de depuracion compilando los ficheros individualmente con <tt/-g/, y enlazandolos sin <tt/-g/. </descrip> <sect> Carga Dinamica <p> <em>Esta seccion es un poco corta; la ire haciendo mas grande.</em> <sect1> 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. <sect1> Mensajes de error <p> ¡Mandame tus errores de enlazado! No los solucionare pero los presentare aqui... <descrip> <tag> <tt>can't load library: /lib/libxxx.so, Incompatible version </tt> No puedo cargar libreria: /lib/libxxx.so, version incompatible</tag> (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: <tscreen><verb> ftp: can't load library 'libreadline.so.2' </verb></tscreen> <tag><tt/warning using incompatible library version xxx/ aviso usando version incompatible de libreria xxx</tag> (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. </descrip> <sect1> Controlando la operacion de carga dinamica <label id="index.80"> <!-- <tt/LD_*/ environment variables --> <label id="index.81"> <!-- ldd --> <p> 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 <tt/ldd/ con algunas opciones. Incluyen: <itemize> <item> <tt/LD_BIND_NOW/ --- normalmente, no se buscan las funciones en las librerias hasta que son llamadas. Fijando esta opcion provoca que se busquen todas las funciones cuando se carga la libreria, haciendo que el tiempo de arranque sea mayor. Es util cuando quieres testear un programa para estar seguro de que se enlaza todo. <item> <tt/LD_PRELOAD/ puede ser fijado para un fichero que contenga definiciones de funciones que sobreescriban otras. Por ejemplo, si estabas testeando estrategias de asignacion de memoria, y querias reemplazar 'malloc', podrias escribir tu rutina reemplazante, compilarla en <tt/malloc.o/ y entonces: <tscreen><verb> $ LD_PRELOAD=malloc.o; export LD_PRELOAD $ some_test_program </verb></tscreen> <tt/LD_ELF_PRELOAD/ y <tt/LD_AOUT_PRELOAD/ son similares, pero se aplican cada una al tipo de binario especifico. Si <tt/LD_/<em/something/<tt/_PRELOAD/ y <tt/LD_PRELOAD/ estan activadas, se usa la mas especifica. <item> <tt/LD_LIBRARY_PATH/ es una lista de directorios separados por dos puntos en los que se buscan librerias compartidas. No afecta a ld; solo tiene efecto en tiempo de ejecucion. Tambien, esta desactivada para programas que ejecutan setuid o setgid. De nuevo, <tt/LD_ELF_LIBRARY_PATH/ y <tt/LD_AOUT_LIBRARY_PATH/ pueden ser usadas para dirigir la busqueda diferentemente segun los distintos binarios. <tt/LD_LIBRARY_PATH/ no sera necesaria para un uso normal; añade los directorios a <tt>/etc/ld.so.conf/</tt> y ejecuta ldconfig mejor. <item> <tt/LD_NOWARN/ se aplica solo a a.out. Cuando esta fijada (p.ej con LD_NOWARN=true; export LD_NOWARN) hace que el cargador no emita avisos no graves (como mensajes sobre incompatibilidad de numero menor de version). LD_WARN se aplica solo a ELF. Cuando esta activada, cambia los mensajes usualmente fatales ''Can't find library'' a avisos. No tiene mucho uso con un uso normal, pero es importante para ldd. <item> <tt/LD_TRACE_LOADED_OBJECTS/ se aplica solo a ELF, y provoca que el programa crea que se esta ejecutando bajo ldd: <tscreen><verb> $ LD_TRACE_LOADED_OBJECTS=true /usr/bin/lynx libncurses.so.1 => /usr/lib/libncurses.so.1.9.6 libc.so.5 => /lib/libc.so.5.2.18 </verb></tscreen> </itemize> <sect1> Escribiendo programas con carga dinamica <label id="index.82"> <!-- <tt/dlopen()/ --> <label id="index.83"> <!-- <tt/dlsym()/ --> <p> 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 <tt/dlopen(3)/, que puede ser encontrada en el paquete ld.so. Aqui hay un bonito ejemplo: enlazalo con <tt>-ldl</tt> <tscreen><verb> #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"); } } </verb></tscreen> <sect> Contactar con los autores <sect1> Informes de fallos <label id="index.84"> <!-- gcc, bugs --> <p> Empieza por <bf/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 <tt/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. <sect1> Ayuda en el desarrollo <p> 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 <tt/linux-gcc@vger.rutgers.edu/. Si lo unico que quieres es saber por donde va la discusion, hay unos archivos en <tt><htmlurl url="http://homer.ncm.com/linux-gcc/" name="http://homer.ncm.com/linux-gcc/"></tt>. El resto de las cosas a hacer dependen de lo que tu quieras hacer. <sect> Final <sect1> Los creditos <p><quote> 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. </quote> 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. <sect1> Traducciones <p> 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. <sect1> Correspondencia <p> Mandame el correo a <tt><htmlurl url="mailto:dan@detached.demon.co.uk" name="dan@detached.demon.co.uk"></tt>. Mi clave publica PGP (ID 5F263625) esta disponible en mis paginas web <tt><htmlurl url="http://ftp.linux.org.uk/~barlow/" name="http://ftp.linux.org.uk/~barlow/"></tt>, si sientes la necesidad de ser secreto con lo que cuentas. <sect1> Apartado Legal <p>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 <tt/<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 <tt/gregh@sunsite.unc.edu/ mediante email. <sect> Indice <p> Las entradas que empiezan con un caracter no alfabetico estan listadas en orden ASCII. <itemize> <item> <tt/-fwritable-strings/ <ref id="index.39" name="39"> <ref id="index.56" name="56"> <item> /lib/cpp <ref id="index.16" name="16"> <item> a.out <ref id="index.1" name="1"> <item> <tt/ar/ <ref id="index.10" name="10"> <item> <tt/as/ <ref id="index.8" name="8"> <item> <asm/*.h> <ref id="index.19" name="19"> <item> <tt/atoi()/ <ref id="index.40" name="40"> <item> <tt/atol()/ <ref id="index.41" name="41"> <item> binaries too big <ref id="index.63" name="63"> <ref id="index.65" name="65"> <ref id="index.77" name="77"> <item> chewing gum <ref id="index.3" name="3"> <item> <tt/cos()/ <ref id="index.68" name="68"> <item> debugging <ref id="index.59" name="59"> <item> <tt/dlopen()/ <ref id="index.82" name="82"> <item> <tt/dlsym()/ <ref id="index.83" name="83"> <item> documentation <ref id="index.4" name="4"> <item> EINTR <ref id="index.52" name="52"> <item> elf <ref id="index.0" name="0"> <ref id="index.71" name="71"> <item> <tt/execl()/ <ref id="index.57" name="57"> <item> <tt/fcntl/ <ref id="index.47" name="47"> <item> <tt/FD_CLR/ <ref id="index.44" name="44"> <item> <tt/FD_ISSET/ <ref id="index.45" name="45"> <item> <tt/FD_SET/ <ref id="index.43" name="43"> <item> <tt/FD_ZERO/ <ref id="index.46" name="46"> <item> <tt/file/ <ref id="index.2" name="2"> <item> <float.h> <ref id="index.20" name="20"> <item> gcc <ref id="index.6" name="6"> <item> <tt/gcc -fomit-frame-pointer/ <ref id="index.61" name="61"> <item> <tt/gcc -g/ <ref id="index.60" name="60"> <item> gcc -v <ref id="index.14" name="14"> <item> gcc, bugs <ref id="index.15" name="15"> <ref id="index.28" name="28"> <ref id="index.29" name="29"> <ref id="index.84" name="84"> <item> gcc, flags <ref id="index.13" name="13"> <ref id="index.25" name="25"> <ref id="index.26" name="26"> <item> gdb <ref id="index.64" name="64"> <item> header files <ref id="index.17" name="17"> <item> interrupted system calls <ref id="index.51" name="51"> <item> <tt/ld/ <ref id="index.9" name="9"> <item> <tt/LD_*/ environment variables <ref id="index.80" name="80"> <item> ldd <ref id="index.81" name="81"> <item> libc <ref id="index.7" name="7"> <item> <tt/libg.a/ <ref id="index.62" name="62"> <item> libgcc <ref id="index.79" name="79"> <item> <limits.h> <ref id="index.21" name="21"> <item> lint <ref id="index.58" name="58"> <item> <linux/*.h> <ref id="index.18" name="18"> <item> manual pages <ref id="index.5" name="5"> <item> <tt/<math.h>/ <ref id="index.70" name="70"> <item> maths <ref id="index.69" name="69"> <item> <tt/mktemp()/ <ref id="index.55" name="55"> <item> optimisation <ref id="index.27" name="27"> <item> QMAGIC <ref id="index.76" name="76"> <item> segmentation fault <ref id="index.30" name="30"> <ref id="index.54" name="54"> <item> segmentation fault, in GCC <ref id="index.33" name="33"> <item> select() <ref id="index.50" name="50"> <item> <tt/SIGBUS/ <ref id="index.34" name="34"> <item> <tt/SIGEMT/ <ref id="index.35" name="35"> <item> <tt/SIGIOT/ <ref id="index.36" name="36"> <item> SIGSEGV <ref id="index.31" name="31"> <ref id="index.53" name="53"> <item> SIGSEGV, in gcc <ref id="index.32" name="32"> <item> <tt/SIGSYS/ <ref id="index.38" name="38"> <item> <tt/SIGTRAP/ <ref id="index.37" name="37"> <item> <tt/sin()/ <ref id="index.67" name="67"> <item> soname <ref id="index.73" name="73"> <item> <tt/sprintf()/ <ref id="index.42" name="42"> <item> statically linked binaries, unexpected <ref id="index.66" name="66"> <ref id="index.78" name="78"> <item> <stdarg.h> <ref id="index.23" name="23"> <item> <stddef.h> <ref id="index.24" name="24"> <item> <tt/strings/ <ref id="index.11" name="11"> <item> <tt><sys/time.h></tt> <ref id="index.48" name="48"> <item> <tt><unistd.h></tt> <ref id="index.49" name="49"> <item> <varargs.h> <ref id="index.22" name="22"> <item> version numbers <ref id="index.12" name="12"> <ref id="index.74" name="74"> <item> weird things <ref id="index.72" name="72"> <item> ZMAGIC <ref id="index.75" name="75"> </itemize> <!-- ************** F I N A L DEL G C C - C O M O **************** --> </article>