Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Problème de chainage de fonction avec la glibc dlsym/RTLD_NEXT

10 réponses
Avatar
Yann LANGLAIS
Bonjour.

J'ai un petit soucis avec un chaînage de fonction avec les fonctions
sous linux debian sarge.

Voici un petit bout ce code (vous pouvez faire du copier/coller direct
vers un shell) correspondant à un lien effectué lors de la compilation :
Code:
for i in 1 2 3 4
do
cat > lib$i.c <<EOF
#include <stdio.h>
#include <dlfcn.h>
void foo() {
void (*next_foo)(void);
printf("lib$i.foo()\n");
if (next_foo = (void (*)(void)) dlsym(RTLD_NEXT, "foo")) next_foo();
}
EOF
gcc -shared -fPIC lib$i.c -o lib$i.so -D_GNU_SOURCE
done
cat > chain.c <<EOF
#include <dlfcn.h>
extern void foo();
int main() {
foo();
return 0;
}
EOF
gcc chain.c -o chain -L. -l1 -l2 -l3 -l4 -ldl
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./chain


J'obtiens bien ce que j'attends :
Code:

lib1.foo()
lib2.foo()
lib3.foo()
lib4.foo()


Seulement, en effectuant les liens dynamiquement en runtime, comme dans
l'exemple suivant :
Code:
cat > chain2.c <<EOF
#include <dlfcn.h>
int main() {
void *l1, *l2, *l3, *l4;
void (*bar)();
l1 = dlopen("lib1.so", RTLD_NOW | RTLD_GLOBAL);
l2 = dlopen("lib2.so", RTLD_NOW | RTLD_GLOBAL);
l3 = dlopen("lib3.so", RTLD_NOW | RTLD_GLOBAL);
l4 = dlopen("lib4.so", RTLD_NOW | RTLD_GLOBAL);
bar = (void (*)()) dlsym(RTLD_DEFAULT, "foo");
bar();
dlclose(l4);
dlclose(l3);
dlclose(l2);
dlclose(l1);
return 0;
}
EOF
gcc chain2.c -o chain2 -ldl -D_GNU_SOURCE
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./chain2


J'obtiens :
Code:

lib1.foo()


Les fonctions "foo" des lib2, lib3 et lib4 ne sont pas prises en compte.

Avez-vous un comportement similaires sous d'autres versions de linux /
ou d'autres Unix ?

Pour information, le même code sous solaris 8 lié avec la libdl.so
native de solaris et compilé avec Forte (compilateur natif) donne le
résultat attendu.

D'avance un grand merci.

Yann LANGLAIS.

10 réponses

Avatar
Laurent Wacrenier
Yann LANGLAIS écrit:
Pour information, le même code sous solaris 8 lié avec la libdl.so
native de solaris et compilé avec Forte (compilateur natif) donne le
résultat attendu.


Avec FreeBSD 6 aussi.

Avatar
langlais
Merci de cette précision.

Quel est l'origine de la libdl sur FreeBSD ?
Est-ce la glibc ou une implémentation native ?
Avatar
Laurent Wacrenier
langlais écrit:
Merci de cette précision.

Quel est l'origine de la libdl sur FreeBSD ?
Est-ce la glibc ou une implémentation native ?


Il n'y a pas de "libdl" sur FreeBSD, les fonctions sont incluses dans
la libc, qui n'est pas glibc.

Si tu veux une API inifiée, qui fonctionnera sur des architectures
sans dlopen() mais avec la possibilité de lier des symoboles au vol,
utilise libltdl, distribué avec libtool (GNU).

Comme ça utilise dlopen() en fond (pour les architectures qui
fonctionnent comme ça), je ne suis pas sûr que les bigs ne seront pas
exportés.

Avatar
langlais
Ok.

En fait, le but des bouts de codes est avant tout d'illustrer
différentes utilisation de la libdl (partie posix + extensions
communes Solaris/Linux) dans un article.

Je suis surpris par ce que je vois sur Linux, d'autant que ca
fonctionne à merveille ailleurs (c'est, lors d'une étape de relecture
intermédiaire, en vérifiant que les scripts et bouts de codes
compilaient sur linux que je me suis apperçu du problème :( ).

Le point intéressant, c'est que je vais pouvoir ajouter un paragraphe
ou deux sur libtool et sa libtld.

Les divers contacts que j'ai eu jusqu'à présent montrent aussi qu'il
serait bon de rajouter un paragraphe traitant de la norme Posix et des
différentes extenstions (dont celles de Solaris et celle de la glibc).

Pour le reste, je ne sais toujours pas si c'est un feature ou un bug de
la glibc, s'il est introduit ou non par les patches debian ou si le
problème est lié à mon installation.

J'a pu observer des résultats encore plus désespérants sur une des
dernières Mandrake (10.2 ?).

Merci.
Avatar
Laurent Wacrenier
langlais écrit:
Je suis surpris par ce que je vois sur Linux, d'autant que ca
fonctionne à merveille ailleurs (c'est, lors d'une étape de relecture
intermédiaire, en vérifiant que les scripts et bouts de codes
compilaient sur linux que je me suis apperçu du problème :( ).


Le manuel de dlopen() sur Linux dit de lier l'executable avec
-rdynamic pour voir les références exterieures. Ça peuty changer
quelque chose. Tu as essayé ?

Avatar
langlais
Non, en effet, je n'ai pas essayé. C'est peut-être une bonne piste.
Je testerai ca ce soir.

Cependant, mon interprètation de l'option -rdynamic est que le
programme lui-même peut alors servir à résoudre les dépendances
d'une bibliothèque chargée dynamiquement, i.e. le programme est lui
même chargé avec RTLD_GLOBAL, i.e.:

<<<<<<<<<<<<<<<<<
l'extrait du man:
External references in the library are resolved using the libraries in
that library's
dependency list and any other libraries previously opened with the
RTLD_GLOBAL flag.

If the executable was linked with the flag "-rdynamic", then the global
symbols in the
executable will also be used to resolve references in a dynamically
loaded library.

ma traduction:
Les références externes dans une bibliothèque sont résolue en
utilisant les bibliothèques de la liste de dépendance de cette
bibliothèque et toute autre bibliothèque précédement ouverte avec
l'option RTLD_GLOBAL.

Si l'exécutable est lié avec l'option "-rdynamic", alors les symboles
globaux dans
l'exécutable seront aussi utilisés pour résoudre les références
dans une bibliothèque
dynamiquement chargée.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
lib.c :
extern void toto(void);
void lib(void) {
toto();
}

exe.c:
#include <dlfcn.h>
#include <stdio.h>
void toto() { printf("in toton"); }
int main() {
void *p;
void (*lalib)();
if (!(p = dlopen("lib.so", RTLD_LAZY))) return 1;
if (!(lalib = (void (*)()) dlsym(p, "lib"))) printf("symbol not
fount : toton");
else lalib();
}

sans -rdynamic => sybol not found : toto.
avec -rdynamic => in toto





Enfin j'essaierai ce soir de toute façon! .



Avatar
Laurent Wacrenier
langlais écrit:
sans -rdynamic => sybol not found : toto.
avec -rdynamic => in toto





Oui, c'est à ça que ça sert d'habitude.




Avatar
Yann LANGLAIS
langlais écrit:

sans -rdynamic => sybol not found : toto.
avec -rdynamic => in toto






Oui, c'est à ça que ça sert d'habitude.



Ben, pas de changement avec ou sans -rdynamic. Donc ca ne sert bien qu'à ça.





Avatar
Laurent Wacrenier
Yann LANGLAIS écrit:
Ben, pas de changement avec ou sans -rdynamic. Donc ca ne sert bien qu'à ça.


regarde ce qui se passe en définissant la variable d'environement LD_DEBUG :

LD_DEBUG=all ./chain2

Avatar
Yann LANGLAIS
Yann LANGLAIS écrit:

Ben, pas de changement avec ou sans -rdynamic. Donc ca ne sert bien qu'à ça.



regarde ce qui se passe en définissant la variable d'environement LD_DEBUG :

LD_DEBUG=all ./chain2



La sortie avec all avait un peu trop de verve pour être facilement
analysable. J'ai restreint à "files symbols" et voilà:

14129: symbol=dlsym; lookup in file=./chain2
14129: symbol=dlsym; lookup in file=/lib/tls/libdl.so.2
14129: symbol=_dl_sym; lookup in file=./chain2
14129: symbol=_dl_sym; lookup in file=/lib/tls/libdl.so.2
14129: symbol=_dl_sym; lookup in file=/lib/tls/libc.so.6
14129: symbol=foo; lookup in file=./chain2
14129: symbol=foo; lookup in file=/lib/tls/libdl.so.2
14129: symbol=foo; lookup in file=/lib/tls/libc.so.6
14129: symbol=foo; lookup in file=/lib/ld-linux.so.2
14129: symbol=foo; lookup in file=lib1.so

--> il trouve bien foo dans lib1.so

lib1.foo()
14129: symbol=foo; lookup in file=/lib/tls/libc.so.6
14129: symbol=foo; lookup in file=/lib/ld-linux.so.2

--> il fait comme si aucune bibliothèque n'était chargée

14129: symbol=dlclose; lookup in file=./chain2
14129: symbol=dlclose; lookup in file=/lib/tls/libdl.so.2
14129: symbol=_dl_close; lookup in file=./chain2
14129: symbol=_dl_close; lookup in file=/lib/tls/libdl.so.2
14129: symbol=_dl_close; lookup in file=/lib/tls/libc.so.6
14129:

On dirait bien un bug !