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

Un bizarrerie à l'édition de lien

10 réponses
Avatar
Dominique MICOLLET
Bonjour,

Je suis peut être un peu hors charte, mais ce forum est celui de
l'arborescence fr qui m'a semblé le plus adapté.

Soit le programme suivant, hello.c :
#include <ncurses.h>

int main()
{
initscr(); /* Start curses mode */
printw("Hello World !!!"); /* Print Hello World */
refresh(); /* Print it on to the real screen */
getch(); /* Wait for user input */
endwin(); /* End curses mode */

return 0;
}


Les commandes de compilation suivantes fonctionnent correctement :
gcc -o hello hello.c -lncurses
gcc -lncurses -o hello hello.c
gcc -static -o hello hello.c -lncurses
sur un debian 4r1 et avec un gcc 4.1.2

Par contre
gcc -static -lncurses -o hello hello.c
me produit une bordée d'erreurs :
/tmp/ccWYtzcj.o: dans la fonction « main »:
hello.c:(.text+0x12): référence indéfinie vers « initscr »
hello.c:(.text+0x1e): référence indéfinie vers « printw »
....
(Je n'ai pas tout recopié)

En soit, ça n'est pas gênant, mais j'aimerais comprendre pourquoi la
position de -lncurses provoque ce comportement et ce uniquement pour la
compilation statique.


Cordialement
--
Dominique MICOLLET Email : enlever deux fr
Universite de Bourgogne
9, Avenue Alain SAVARY BP 47870 Tel : +33/(0)3-80-39-59-27
21078 DIJON CEDEX FRANCE Tfx : +33/(0)3-80-39-68-69

10 réponses

Avatar
Yann Renard
Dominique MICOLLET wrote:
Bonjour,

Je suis peut être un peu hors charte, mais ce forum est celui de
l'arborescence fr qui m'a semblé le plus adapté.

Soit le programme suivant, hello.c :
#include <ncurses.h>

int main()
{
initscr(); /* Start curses mode */
printw("Hello World !!!"); /* Print Hello World */
refresh(); /* Print it on to the real screen */
getch(); /* Wait for user input */
endwin(); /* End curses mode */

return 0;
}


Les commandes de compilation suivantes fonctionnent correctement :
gcc -o hello hello.c -lncurses
gcc -lncurses -o hello hello.c
gcc -static -o hello hello.c -lncurses
sur un debian 4r1 et avec un gcc 4.1.2

Par contre
gcc -static -lncurses -o hello hello.c
me produit une bordée d'erreurs :
/tmp/ccWYtzcj.o: dans la fonction « main »:
hello.c:(.text+0x12): référence indéfinie vers « initscr »
hello.c:(.text+0x1e): référence indéfinie vers « printw »
....
(Je n'ai pas tout recopié)

En soit, ça n'est pas gênant, mais j'aimerais comprendre pourquoi la
position de -lncurses provoque ce comportement et ce uniquement pour la
compilation statique.


Cordialement


GCC résoud les symboles dans l'ordre ou ils sont requis et dans l'ordre
ou ils sont fournis, c'est à dire qu'un symbole manquant dans une
librairie sera recherché dans les librairies qui le suivent mais pas
celles qui le précèdent. Du coup, les symboles manquants dans "hello" ne
sont pas cherchés dans "libncurses" car le flag -l est donné avant.
L'ordre des librairies a lui aussi une importance si les librairies ont
des interdépendances.

Yann

Avatar
Dominique MICOLLET
Yann Renard wrote:

GCC résoud les symboles dans l'ordre ou ils sont requis et dans l'ordre
ou ils sont fournis, c'est à dire qu'un symbole manquant dans une
librairie sera recherché dans les librairies qui le suivent mais pas
celles qui le précèdent. Du coup, les symboles manquants dans "hello" ne
sont pas cherchés dans "libncurses" car le flag -l est donné avant.
L'ordre des librairies a lui aussi une importance si les librairies ont
des interdépendances.


Admettons (je suis d'accord avec "l'ordre fournis" mais je doute
pour "l'ordre requis") : cela explique que la compilation statique echoue.

Mais cela amène une autre question : pourquoi l'édition réussit-elle en
compilation dynamique (2ième commande).



--
Cordialement

Dominique MICOLLET Email : enlever deux fr
Universite de Bourgogne
9, Avenue Alain SAVARY BP 47870 Tel : +33/(0)3-80-39-59-27
21078 DIJON CEDEX FRANCE Tfx : +33/(0)3-80-39-68-69

Avatar
Aris
Yann Renard wrote:

GCC résoud les symboles dans l'ordre ou ils sont requis et dans l'ordre
ou ils sont fournis, c'est à dire qu'un symbole manquant dans une
librairie sera recherché dans les librairies qui le suivent mais pas
celles qui le précèdent. Du coup, les symboles manquants dans "hello" ne
sont pas cherchés dans "libncurses" car le flag -l est donné avant.
L'ordre des librairies a lui aussi une importance si les librairies ont
des interdépendances.


Admettons (je suis d'accord avec "l'ordre fournis" mais je doute
pour "l'ordre requis") : cela explique que la compilation statique echoue.

Mais cela amène une autre question : pourquoi l'édition réussit-elle en
compilation dynamique (2ième commande).



parce que le liage dynamique est plus simple, il se contente de voir si

tous les symboles sont là et laisse le lieur dynamique se charger du
linking au moment où l'on execute le programme.

c'est comme ça que je le vois en tout cas.


Avatar
Antoine Leca
En news:470dfdfc$0$13307$,
Dominique MICOLLET va escriure:
Yann Renard wrote:

GCC résoud les symboles dans l'ordre ou ils sont requis et dans



Il n'y a pas que GCC ! En fait sous *nix tous les compilos C font cela.

l'ordre ou ils sont fournis, c'est à dire qu'un symbole manquant
dans une librairie sera recherché dans les librairies qui le suivent
mais pas celles qui le précèdent.


cela explique que la compilation statique echoue.

Mais cela amène une autre question : pourquoi l'édition réussit-elle
en compilation dynamique


En édition de liens dynamique, lors de l'appel du lieur statique (celui qui
est appelé par le compilo C, collect pour GCC), « l'instruction » -lncurses,
sachant qu'il existe un truc nommé libncurses.so.N.M.P sur ta machine, a
seulement pour effet de rajouter dans l'exécutable "hello" un truc comme:
À l'édition des liens (la vraie), on cherchera aussi dans l'objet
partagé libncurses.so.N, sous-version au moins M

Un peu plus tard, au début de l'exécution du programme "hello", le lieur
dynamique (rtld, ld.so, etc.) chargera en même temps libncurses.so.N.Q.R
(Q>=M), y trouvera les symboles qu'il faut et sera content.

Autrement dit, à la compilation il n'y a pas de vraie vérification que le
symbole existe réellement dans la bibliothèque (partagée). Entre autre
raisons, parce que la bibliothèque réellement chargée est peut-être
indisponible (imagine le cas où il y a des dépendances circulaires).


L'ordre pour l'édition statique est en fait un truc de développement (qui
permet d'interposer une bibliothèque avant les bibliothèques normales ;
exemple typique, une bibliothèque avec une version de malloc() qui s'épanche
verbeusement sur l'allocation dynamique de ton programme). Les développeurs
*nix aiment tellement ce truc qu'ils l'ont mis à toutes les sauces, au point
qu'avec l'édition dynamique on peut jouer sur l'ordre de chargement via les
commandes dans l'exécutable, mais aussi avec le chemin de recherche des
objets partagés ; et on peut jouer aussi dans le détail avec les numéros de
versions (et je ne rentrerai pas dans les détails, cela dépasse le cadre
assigné à cet article ; d'ailleurs j'ai déjà passablement simplifié, voire
fait des approximations).


Antoine


Avatar
Dominique MICOLLET
Antoine Leca wrote:

Autrement dit, à la compilation il n'y a pas de vraie vérification que le
symbole existe réellement dans la bibliothèque (partagée).


Hum..... J'ai un doute

Si je compile un programme avec un appel à la fonction truc(), ni déclarée
ni définie, le compilateur me retourne un message d'erreur. Il vérifie donc
l'existence du symbole.





Entre autre
raisons, parce que la bibliothèque réellement chargée est peut-être
indisponible (imagine le cas où il y a des dépendances circulaires).


Dans ce cas c'est à l'exécution que ce produira l'erreur.

Et il me semble que cela ne peut se produire que si les libxx.so de
développement sont différentes des libxx.so d'exécution. C'est possible si
le binaire est portée d'une machine à une autre, mais peu probable pour une
même machine.

Et pour ma question, elles sont OK.





L'ordre pour l'édition statique est en fait un truc de développement (qui
permet d'interposer une bibliothèque avant les bibliothèques normales ;
exemple typique, une bibliothèque avec une version de malloc() qui
s'épanche verbeusement sur l'allocation dynamique de ton programme).


C'est aussi le cas en compilation dynamique : c'est la première occurence du
symbole dans l'ordre d'examen des bibliothèques qui est choisi.


Je reste dubitatif quant aux causes exactes de cette "erreur" de
compilation.

Quoiqu'il en soit, merci.


--
Cordialement

Dominique MICOLLET Email : enlever deux fr
Universite de Bourgogne
9, Avenue Alain SAVARY BP 47870 Tel : +33/(0)3-80-39-59-27
21078 DIJON CEDEX FRANCE Tfx : +33/(0)3-80-39-68-69

Avatar
Antoine Leca
En news:470f341b$0$5144$, Dominique MICOLLET va
escriure:
Antoine Leca wrote:

Autrement dit, à la compilation il n'y a pas de vraie vérification
que le symbole existe réellement dans la bibliothèque (partagée).


Hum..... J'ai un doute

Si je compile un programme avec un appel à la fonction truc(), ni
déclarée ni définie, le compilateur me retourne un message d'erreur.
Il vérifie donc l'existence du symbole.


Si c'est réellement un message du compilateur, cela sort de ce que
j'expliquais (je parlais seulement de l'édition des liens).

Si c'est effectivement un message de l'éditeur des liens, c'est parce que le
tien fait comme cela, d'autre font autrement ; et c'est justement pour cela
que je ne voulais pas entrer dans les détails :-B


Entre autre
raisons, parce que la bibliothèque réellement chargée est peut-être
indisponible (imagine le cas où il y a des dépendances circulaires).


Dans ce cas c'est à l'exécution que ce produira l'erreur.


Voui.


Et il me semble que cela ne peut se produire que si les libxx.so de
développement sont différentes des libxx.so d'exécution.


« que » non, mais c'est effectivement la raison la plus fréquente.

C'est possible si le binaire est portée d'une machine à une autre,
mais peu probable pour une même machine.


Pas seulement. Un autre cas typìque, c'est celui où les chemins de recherche
sont différents entre compilation et exécution, dû par exemple à une
configuration douteuse ou incorrecte de libtool (un exemple au hasard...),
ou bien dès que l'on déplace « pas comme il faut » l'exécutable, et donc les
« bonnes » bibliothèques, référencées dans l'exécutable, ne sont pas
trouvées au moment où on lance l'exécutable.



L'ordre pour l'édition statique est en fait un truc de développement
(qui permet d'interposer une bibliothèque avant les bibliothèques
normales ; exemple typique, une bibliothèque avec une version de
malloc() qui s'épanche verbeusement sur l'allocation dynamique de
ton programme).


C'est aussi le cas en compilation dynamique : c'est la première
occurence du symbole dans l'ordre d'examen des bibliothèques qui est
choisi.


Ce qui change, c'est l'ordre.


Je reste dubitatif quant aux causes exactes de cette "erreur" de
compilation.


L'erreur (la bordée d'erreurs) t'a été expliqué par ailleurs.

Ta deuxième question était le pourquoi de la non-erreur pour la version en
liaison dynamique.
Outre une toujours possible erreur de programmation du (des) lieur(s) que tu
utilises, la cause qui me semble la plus logique est celle que j'ai
expliquée, que les symboles (ici initscr, printw etc.) aient leurs
références satisfaites en édition dynamique (parce que l'ordre -lncurses
signifie « ajoute inconditionnellement l'objet libcurses.so dans l'espace
virtuel ») mais pas en édition statique (où -lncurses signifie réellement
« cherche dans la bibliothèque libncurses.a les symboles actuellement non
résolus ; comme il n'y en a pas, ne fait rien... »)
S'il faut le considérer comme une erreur ou pas, je n'en sais rien.

Après, le reste de mes explications est de la broderie sur le sujet.


Antoine


Avatar
Thierry PINELLI
Dominique MICOLLET wrote:

Si je compile un programme avec un appel à la fonction truc(), ni déclarée
ni définie, le compilateur me retourne un message d'erreur. Il vérifie donc
l'existence du symbole.


Non, c'est l'éditeur de lien qui ne pourra résoudre le symbole s'il
n'est pas dans une bibliothèque qu'il connait (par défaut).

/home/tpinelli/devel/C>cat hello.c

int truc(void);
int main(void)
{
return(truc());
}


/home/tpinelli/devel/C>cc -Wall -c hello.c -o hello.o
/home/tpinelli/devel/C>

je n'ai pas d'erreur

le compilateur s'est royalement contrefiché de la résolution de truc()

c'est normal

Avatar
Dominique MICOLLET
Thierry PINELLI wrote:
Non, c'est l'éditeur de lien qui ne pourra résoudre le symbole s'il
n'est pas dans une bibliothèque qu'il connait (par défaut).
.....

le compilateur s'est royalement contrefiché de la résolution de truc()

c'est normal


J'ai effectivement fait un abus de langage en
assimilant "compilation+edition de liens" à "compilation".

Je différenciais, mal, en fait "compilation+edition statique"
de "compilation+edition dynamique".

Malgré les diverses explications qui m'ont été données, j'ai encore du mal à
avoir une vue synthétique de tout cela (en particulier s'il y a des appels
croisés entre bibliothèques). Il faut que je fasse quelques essais simples
dans mon coin pour mieux appréhender tout cela. Il faut déjà que j'apprenne
à fabriquer des bibliothèques dynamiques.



--
Cordialement

Dominique MICOLLET Email : enlever deux fr
Universite de Bourgogne
9, Avenue Alain SAVARY BP 47870 Tel : +33/(0)3-80-39-59-27
21078 DIJON CEDEX FRANCE Tfx : +33/(0)3-80-39-68-69

Avatar
Antoine Leca
En news:47145ad1$0$2148$, Dominique MICOLLET va
escriure:
Thierry PINELLI wrote:
Non, c'est l'éditeur de lien qui ne pourra résoudre le symbole s'il
n'est pas dans une bibliothèque qu'il connait (par défaut).
.....

le compilateur s'est royalement contrefiché de la résolution de
truc()

c'est normal


J'ai effectivement fait un abus de langage en
assimilant "compilation+edition de liens" à "compilation".


En réalité, c'est nous (Thierry et moi) qui faisons des abus de langage.
En C, le compilateur est l'ensemble, préprocesseur+« translateur » (faute
d'un meilleur terme)+éditeur des liens. En tous cas, c'est ce que prétendent
les normes :-)

Maintenant, dans le langage courant et à cause de l'historique, on utilise
souvent le terme compilateur pour désigner seulement la partie centrale,
baptisée « translateur » ci-dessus (techniquement la plus complexe à
réaliser et l'objet du maximum d'attention en cours de « compilation », avec
les sous-modules classiques d'analyse lexicale, syntaxique, de génération de
code, etc.)

Bon, c'est du pinaillage et cela ressemble de près à la minute du professeur
Nimbus, mais ta phrase m'a fait réagir :-)


Je différenciais, mal, en fait "compilation+edition statique"
de "compilation+edition dynamique".


Ouaip. Et de fait, les éditeurs de liens sont passablement différents entre
les deux cas, même si dans la pratique ils sont souvent englobés dans le
même programme.

Malgré les diverses explications qui m'ont été données, j'ai encore
du mal à avoir une vue synthétique de tout cela (en particulier s'il
y a des appels croisés entre bibliothèques). Il faut que je fasse
quelques essais simples dans mon coin pour mieux appréhender tout
cela.


Il y a un bouquin en anglais qui détaille bien (je veux dire de manière
didactique) ce sujet, écrit par John Levine, le modérateur de
news:comp.compilers ; une version brouillon est disponible en ligne
http://www.iecc.com/linker/. Comme d'habitude avec ce genre de livres, il y
a des exercices proposés, c'est donc un bon support pour apprendre (ceci
dit, je n'ai pas fait moi-même les exercices, donc pas taper). Il y a un
penchant pour détailler les formats « historiques » (un mot poli pour dire
obsolètes), mais il aborde bien ELF, sans entrer dans les détails scabreux
(pour cela, voir la littérature de U. Drepper.)


Il faut déjà que j'apprenne à fabriquer des bibliothèques dynamiques.


Hmm, attention, c'est plus difficile que de les utiliser. De plus, la
plupart des gens utilise libtool pour mâcher le boulot, et libtool est un
monde en soi, qui ne facilite pas la compréhension globale, bien au
contraire (ÀMHA).


Antoine


Avatar
Thierry PINELLI
Dominique MICOLLET wrote:

J'ai effectivement fait un abus de langage en
assimilant "compilation+edition de liens" à "compilation".


J'ai effectivement été un peu vicelard en mettant un -c (compile only)
Il ne pouvait pas y avoir d'erreur :)