dan@detached.demon.co.uk
x4354281@turing.ugr.es
)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.
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.
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 :-)
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
.
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).
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:
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.
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.
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:
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.
box
. No es importante en absoluto, y puede poner algo más
(como slackware
o debian
) o nada en absoluto (asi que el nombre
completo del directorio es i486-linux
). Si compilas tu propio gcc,
puedes configurarlo en tiempo de compilación para un efecto ornamental.
Como yo hice :-)
linux
. Puede ser linuxelf
o linuxaout
, y,
el significado de cada uno de ellos varia segun la version que estes
usando.
linux
significa ELF si la version es la 2.7.0 o mas
reciente, a.out en otro caso.
linuxaout
quiere decir a.out. Fue introducido cuando la definicion
de linux
se cambio de a.out a ELF, asi que no veras ningun linuxaout
gcc mas antiguo que el 2.7.0.
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.
2.7.2
es el numero de version.Asi que, en resumen, tengo el gcc 2.7.2 produciendo codigo ELF. Vaya sorpresa.
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:
/usr/lib/gcc-lib/
target/
version/
(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.
/usr/bin/gcc
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 gcc -v
. Para forzarlo a usar otra version, escribe gcc -V
version.
Por ejemplo
# 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
/usr/
target/(bin|lib|include)/
. Si tienes
instalado soporte para distintos ejecutables (por ejemplo, a.out y elf), o un
compilador cruzado, las librerias, binutils (as
, 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 /usr/(bin|lin|include)
.
/lib/
,/usr/lib
y otros son los directorios en
los que estan las librerias para el sistema nativo. Tambien necesitaras
/lib/cpp
para muchas aplicaciones (X hace bastante uso de el)
-- o copialo de /usr/lib/gcc-lib/
target/
version/
o haz que un enlace simbolico apunte aqui.
Aparte de lo que tu instales bajo /usr/local/include
, hay tres
fuentes principales de ficheros de cabecera en Linux.
/usr/include/
y sus subdirectorios tienen
el paquete binario libc de H J Lu. Digo la mayoria porque tambien puedes
tener ficheros de otras fuentes (librerias dbm
, por ejemplo) aqui,
especialmente si estas usando la distribucion libc mas reciente (que no
viene con dbm, como las antiguas).
/usr/include/linux
y /usr/include/asm
(para
los ficheros <linux/*.h>
y <asm/*.h>
)
deberian ser enlaces simbolicos a los directorios linux/include/linux
y linux/include/asm
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 make config
en el directorio del kernel despues de descomprimir los fuentes.
Muchos ficheros dependen de <linux/autoconf.h>
que de
otra manera no existiria, y en algunas versiones recentes del kernel asm
es un enlace simbolico a el mismo y solo se crea cuando se hace el make config
.
Asi que si descomprimes los fuentes del kernel en /usr/src/linux,
$ 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 .
<float.h>
, <limits.h>
,
<varargs.h>
, <stdarg.h>
y <stddef.h>
varian con la version del compilador, asi que se encuentran en
/usr/lib/gcc-lib/i486-box-linux/2.7.2/include/
o similar.
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/
.
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.
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.
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.
gcc puede optimizar el codigo que genera si añadimos -O
n
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
.
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.
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.
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>
.
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 #ifdef
s apropiados:
#ifdef SIGSYS
/* ... el codigo no POSIX para SIGSYS va aqui .... */
#endif
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.
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.
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.
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.
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:
select()
, pause()
, connect()
,
accept()
, read()
en terminales, sockets, pipes o
ficheros en /proc
, write()
en terminales, sockets, pipes o
la impresora, open()
on FIFOs, PTYs or serial lines,
ioctl()
on terminales, fcntl()
con el comando
F_SETLKW
, wait4()
, syslog()
, y cualquier operación TCP o NFS.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
.
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.
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
).
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
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"
.
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/
.
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 }
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
fs
binfmt_aout.c/ y fs
binfmt_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.
-p
, y necesitaras tambien gprof
(del paquete binutils). Mira la pagina del manual para gprof
para los detalles.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.
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)
".
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.
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
.
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 (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.
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.
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 )
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.
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.
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.
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.
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.
!Mandame tus problemas de enlazado! Probablemente no los solucionare pero los escribire si tengo bastante...
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.
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.
__NEEDS_SHRLIB_libc_4
son otra consecuencia del mismo problema.
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
.
Esta seccion es un poco corta; la ire haciendo mas grande.
¡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.
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:
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.
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 malloc.o
y entonces:
$ LD_PRELOAD=malloc.o; export LD_PRELOAD
$ some_test_program
LD_ELF_PRELOAD
y LD_AOUT_PRELOAD
son similares, pero se aplican
cada una al tipo de binario especifico. Si LD_
something_PRELOAD
y
LD_PRELOAD
estan activadas, se usa la mas especifica.
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, LD_ELF_LIBRARY_PATH
y
LD_AOUT_LIBRARY_PATH
pueden ser usadas para dirigir la busqueda
diferentemente segun los distintos binarios. LD_LIBRARY_PATH
no
sera necesaria para un uso normal; añade los directorios a /etc/ld.so.conf/
y ejecuta ldconfig mejor.
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.
LD_TRACE_LOADED_OBJECTS
se aplica solo a ELF, y provoca que el
programa crea que se esta ejecutando bajo ldd:
$ 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
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");
}
}
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.
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.
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.
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.
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.
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.
Las entradas que empiezan con un caracter no alfabetico estan listadas en orden ASCII.
-fwritable-strings
39
56 ar
10 as
8 atoi()
40 atol()
41 cos()
68 dlopen()
82 dlsym()
83 execl()
57 fcntl
47 FD_CLR
44 FD_ISSET
45 FD_SET
43 FD_ZERO
46 file
2 gcc -fomit-frame-pointer
61 gcc -g
60 ld
9 LD_*
environment variables
80 libg.a
62 <math.h>
70 mktemp()
55 SIGBUS
34 SIGEMT
35 SIGIOT
36 SIGSYS
38 SIGTRAP
37 sin()
67 sprintf()
42 strings
11 <sys/time.h>
48 <unistd.h>
49