Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

undefined symbol in foreign library

1 view
Skip to first unread message

card.l...@aliceadsl.fr

unread,
Apr 21, 2008, 12:04:38 PM4/21/08
to
Bonjour,

Je vous soumets un problème posé par le comportement nouveau du programme
que je développe depuis des mois mais sur lequel je tourne en rond depuis
trois jours.
Il s'agit d'un développement parallèle en C et en lisp sur une question de
traitement du signal. Il s'agirait de deux développements séparés à ceci
près : l'utilisation d'une bibliothèque commune à l'un et à l'autre, et qui
se développe en même temps que les deux "mains".
Voici un schéma de l'arborescence :
~/projet_C...autotools (configure, make, etc...
~/projet_C/src...main et interfaces utilisateur
~/projet_C/lib...la bibliothèque en question
~/projet_lisp...quelques packages répondant en gros au même schéma que
projet_C/src dont un package pour interfacer les fonctions étrangères
situées dans la bibliothèque.
~/bin
~/lib
~/...

La bibliothèque qui traite en fait les fonctions de plus bas niveau du
projet, utilise évidemment d'autres bibliothèques dont la gsl qui fournit
une partie du bataillon des algorithmes de calcul. Il y a un bon moment
qu'il n'y a plus guère d'évolution de ce côté et pourtant en réorganisant
mes répertoires j'ai fait apparaître un phénomène que je n'arrive pas à
régler :
##########################################################################
:~/...clisp -K full -L french gceler.lisp
*** - FFI::FIND-FOREIGN-FUNCTION: Cannot open library
".../lib/libceler.so":
".../lib/libceler.so: undefined symbol:gsl_fft_real_wavetable_float_alloc"
##########################################################################
En scrutant le binaire de libceler on trouve bien le symbole en question qui
d'ailleurs n'est pas formellement utilisé par les fonctions et variables
étrangères. De plus la conception est entièrement opaque selon le modèle
Objet aussi loin que le permet le "plain" C.

Merci à ceux qui pourraient me mettre sur la voie.
AMn

Pascal Bourguignon

unread,
Apr 21, 2008, 6:40:54 PM4/21/08
to card.l...@aliceadsl.fr
"card.l...@aliceadsl.fr" <card.l...@aliceadsl.fr> writes:

Est-on sur que la libgsl est déjà chargée dans clisp?


--
__Pascal Bourguignon__ http://www.informatimago.com/

"Klingon function calls do not have "parameters" -- they have
"arguments" and they ALWAYS WIN THEM."

card.l...@aliceadsl.fr

unread,
Apr 22, 2008, 2:09:21 AM4/22/08
to
Non, je ne charge que la lib du projet et appelle les fonctions (meme pas de
variable en fait) du projet. Je me suis effectivement demandé s'il n'était
pas nécessaire de déclarer libgsl également, mais alors pourquoi pas la
libc, et toutes les autres ? j'utilise beaucoup printf pour vérifier
l'autre côté ; ça ne pourrait pas marcher.
Merci de vous intéresser à mon problème.
Je pars en voyage pour la journée. Si vous la gentillesse de poursuivre le
fil, ne pourrais vous répondre que demain.
AMn

card.lemoine

unread,
Apr 22, 2008, 5:21:49 PM4/22/08
to
Pascal Bourguignon a écrit :

> Est-on sur que la libgsl est déjà chargée dans clisp?
>
>

Pardon je crois que j'ai compris votre question de travers.
Qu'entendez-vous par "déjà chargée" ? On donne le chemin de la libgsl,
puis le moyen de pointer sur des fonctions ou des variables de cette
bibliothèque avec les formats des paramètres et valeurs de retour. De
mémoire je crois que c'est réalisé avec les fonctions posix "dynamic
link" [dlfcn(), dlopen(), etc...]. Dans le cas de mon problème les mêmes
composants fonctionnaient bien, avant que je change les paths d'accès à
ma bibliothèque [reposant maintenant en ~/lib figurant dans $LD_LIB_PATH
(j'ai plus le nom exact en tête)] dont certaines fonctions font
elles-même appel à la libgsl (reposant en /usr/local/lib).
Par ailleurs les fichiers lisp ne sont pas encore compilés afin d'éviter
la rémanence de situations antérieures.
Revenant sur la libgsl, la plainte à l'exécution concerne la première
invocation rencontrée dans le fichier qui utilise la libgsl (allocation
d'un buffer de travail). A noter que les pointeurs de ces buffers, au
nombre de trois (wavetable, real, halfcomplex) font partie de la
structure opaque de cette classe. Je l'ai souhaité ainsi après avoir
longuement hésité car en faire des variables de classe plutôt que
d'instance conduisait à figer les dimensions d'un paramètre de l'objet ;
génant pour l'application.
A noter encore qu'il fut un temps où je me souviens avoir eu des
problèmes de segmentation à cause de ces paramètres. Inexpliqués
(changeant l'ordre des trois invocations faisait disparaitre le
problème. Passé le programme à la moulinette de "electric fence" ne
permettait pas de déceler d'anomalies ??? Pas très sain j'en conviens
mais c'était il y a très longtemps et depuis tout semblait tourner comme
une horloge jusqu'à ce que ... vous connaissez la suite, et ce n'est
plus du lisp mais du C.
Est-ce que c'est plus proche de votre question ?
AMn

card.lemoine

unread,
Apr 22, 2008, 5:26:30 PM4/22/08
to
card.lemoine a écrit :

> Pascal Bourguignon a écrit :
>
>> Est-on sur que la libgsl est déjà chargée dans clisp?
>>
>>
> Pardon je crois que j'ai compris votre question de travers.
> Qu'entendez-vous par "déjà chargée" ? On donne le chemin de la ***libgsl***

NON DE _MA_ BIBLIOTHEQUE

Pascal Bourguignon

unread,
Apr 22, 2008, 7:28:45 PM4/22/08
to
"card.lemoine" <st_ge...@tele.fr> writes:

> Pascal Bourguignon a écrit :
>
>> Est-on sur que la libgsl est déjà chargée dans clisp?
>>
> Pardon je crois que j'ai compris votre question de travers.
> Qu'entendez-vous par "déjà chargée" ?


Les dépendances d'une bibliothèque ne sont pas chargées
automatiquement. Il faut les charger explicitement.


Note, j'utilise un chemin absolu dans mon exemple, mais on peut aussi
passer par LD_LIBRARY_PATH:

(setf (ext:getenv "LD_LIBRARY_PATH")
(format nil "/where/my/libs/are:~A" (ext:getenv "LD_LIBRARY_PATH")))
(FFI:DEF-CALL-OUT base-fun (:NAME "base_fun")
(:ARGUMENTS (SIZE FFI:INT :IN))
(:RETURN-TYPE nil)
(:LANGUAGE :STDC)
(:library "libbase.so"))

C/USER[16]> (cat "test.lisp")

(format t ";; -- Loading only libmid.so~%")
(handler-case (FFI:DEF-CALL-OUT mid-fun (:NAME "mid_fun")
(:ARGUMENTS (SIZE FFI:INT :IN))
(:RETURN-TYPE nil)
(:LANGUAGE :STDC)
(:library "/home/pjb/src/lisp/clisp-notes/two-libs/libmid.so"))
(error (err) (princ err)))

(format t ";; -- Loading libbase.so first.~%")
(FFI:DEF-CALL-OUT base-fun (:NAME "base_fun")
(:ARGUMENTS (SIZE FFI:INT :IN))
(:RETURN-TYPE nil)
(:LANGUAGE :STDC)
(:library "/home/pjb/src/lisp/clisp-notes/two-libs/libbase.so"))

(format t ";; -- Then loading libmid.so first.~%")
(FFI:DEF-CALL-OUT mid-fun (:NAME "mid_fun")
(:ARGUMENTS (SIZE FFI:INT :IN))
(:RETURN-TYPE nil)
(:LANGUAGE :STDC)
(:library "/home/pjb/src/lisp/clisp-notes/two-libs/libmid.so"))

(format t ";; -- Now, calling external functions:~%")
(base-fun 42)
(mid-fun 17)

(format t ";; -- Unloading libraries.~%")
(FFI:CLOSE-FOREIGN-LIBRARY "/home/pjb/src/lisp/clisp-notes/two-libs/libmid.so")
(FFI:CLOSE-FOREIGN-LIBRARY "/home/pjb/src/lisp/clisp-notes/two-libs/libbase.so")


C/USER[17]> (load"test.lisp")
;; Loading file test.lisp ...
;; -- Loading only libmid.so
FFI::FIND-FOREIGN-FUNCTION: Cannot open library "/home/pjb/src/lisp/clisp-notes/two-libs/libmid.so": "/home/pjb/src/lisp/clisp-notes/two-libs/libmid.so: undefined symbol: base_fun"
;; -- Loading libbase.so first.
;; -- Then loading libmid.so first.
;; -- Now, calling external functions:
base_fun(42) called.
mid_fun(17) entry.
base_fun(17) called.
mid_fun(17) exit.
;; -- Unloading libraries.
;; Loaded file test.lisp
T
C/USER[18]> (cat "Makefile")
all: libbase.so libmid.so test-c
file libbase.so libmid.so
nm libbase.so
nm libmid.so


test-c:test-c.o
gcc -fPIC -o test-c test-c.o libbase.so libmid.so

test-c.o:test-c.c
gcc -fPIC -shared -o test-c.o test-c.c

base.o:base.c
gcc -fPIC -shared -o base.o base.c

mid.o:mid.c
gcc -fPIC -shared -o mid.o mid.c

libbase.a:base.o
ar cr libbase.a base.o
ranlib libbase.a

libmid.a:mid.o
ar cr libmid.a mid.o
ranlib libmid.a

libbase.so:base.o
gcc -fPIC -shared -Wl,-soname,libbase.so -o libbase.so base.o

libmid.so:mid.o
gcc -fPIC -shared -Wl,-soname,libmid.so -o libmid.so mid.o


clean:
rm -f *.a *.so test-c

C/USER[19]> (cat "base.c")
#include <stdio.h>

extern void base_fun(int i){
printf("base_fun(%d) called.\n",i);
}

C/USER[20]> (cat "mid.c")
#include <stdio.h>

extern void base_fun(int i);

extern void mid_fun(int i){
printf("mid_fun(%d) entry.\n",i);
base_fun(i);
printf("mid_fun(%d) exit.\n",i);
}


C/USER[21]> (cat "test-c.c")

extern void base_fun(int i);
extern void mid_fun(int i);

int main(void){
base_fun(42);
mid_fun(17);
return(0);
}

C/USER[22]>


--
__Pascal Bourguignon__ http://www.informatimago.com/

NEW GRAND UNIFIED THEORY DISCLAIMER: The manufacturer may
technically be entitled to claim that this product is
ten-dimensional. However, the consumer is reminded that this
confers no legal rights above and beyond those applicable to
three-dimensional objects, since the seven new dimensions are
"rolled up" into such a small "area" that they cannot be
detected.

card.lemoine

unread,
Apr 23, 2008, 7:25:52 PM4/23/08
to
Pascal Bourguignon a écrit :

> "card.lemoine" <st_ge...@tele.fr> writes:
>
>> Pascal Bourguignon a écrit :
>>
>>> Est-on sur que la libgsl est déjà chargée dans clisp?
>>>
>> Pardon je crois que j'ai compris votre question de travers.
>> Qu'entendez-vous par "déjà chargée" ?
>
>
> Les dépendances d'une bibliothèque ne sont pas chargées
> automatiquement. Il faut les charger explicitement.
>
Même en faisant coller les codes de votre démo plus près de ma situation
(ci-dessous), cela se déroule comme comme vous le dites. Et pendant près
d'un an cela a fonctionné je ne sais comment. Je suis éloigné de mes
bases toute cette semaine et le début de l'autre. Je vous remercie
beaucoup et vais essayer de comprendre (ou de m'habituer) à ces
comportements.
>

/* mid.c */
#include <stdio.h>

extern int base_fun(int i);
extern int mid_fun (int i) {
int j=base_fun(i);
printf("mid_fun exécute %d -> %d\n",
j, i);
return j>>=1;
}

/* base.c */
#include <stdio.h>

extern int base_fun (int i) {
int j;
printf("base_fun exécute %d -> %d\n", i, j=i<<1);
return j;
}

/* test-c.c */
#include <stdio.h>

extern int mid_fun(int i);
int main(void) {
int i=2;
printf("appel de mid-fun(%d) rend %d\n",i,mid_fun(i));
return (0);
}

;;; test.lisp

;;(format t ";; -- Loading only libmid.so~%")


;;(handler-case (FFI:DEF-CALL-OUT mid-fun (:NAME "mid_fun")
;; (:ARGUMENTS (SIZE FFI:INT :IN))

;; (:RETURN-TYPE FFI:INT)
;; (:LANGUAGE :STDC)
;; (:library "/home/alain/Lisp/pjb/libmid.so"))
;; (error (err) (princ err)))

;; (format t ";; -- Loading libbase.so first.~%")
;; (FFI:DEF-CALL-OUT base-fun (:NAME "base_fun")
;; (:ARGUMENTS (SIZE FFI:INT :IN))

;; (:RETURN-TYPE FFI:INT)
;; (:LANGUAGE :STDC)
;; (:library "/home/alain/Lisp/pjb/libbase.so"))

(format t ";; -- Then loading libmid.so~%")


(FFI:DEF-CALL-OUT mid-fun (:NAME "mid_fun")
(:ARGUMENTS (SIZE FFI:INT :IN))

(:RETURN-TYPE FFI:INT)
(:LANGUAGE :STDC)
(:library "/home/alain/Lisp/pjb/libmid.so"))

(format t ";; -- Now, calling external functions:~%")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (base-fun 42)

(mid-fun 2)

(format t ";; -- Unloading libraries.~%")

(FFI:CLOSE-FOREIGN-LIBRARY "/home/alain/Lisp/pjb/libmid.so")
;; (FFI:CLOSE-FOREIGN-LIBRARY "/home/alain/Lisp/pjb/libbase.so")

Pascal Bourguignon

unread,
Apr 23, 2008, 8:41:44 PM4/23/08
to
"card.lemoine" <card.l...@wanadoo.fr> writes:

> Pascal Bourguignon a écrit :
>> "card.lemoine" <st_ge...@tele.fr> writes:
>>
>>> Pascal Bourguignon a écrit :
>>>
>>>> Est-on sur que la libgsl est déjà chargée dans clisp?
>>>>
>>> Pardon je crois que j'ai compris votre question de travers.
>>> Qu'entendez-vous par "déjà chargée" ?
>> Les dépendances d'une bibliothèque ne sont pas chargées
>> automatiquement. Il faut les charger explicitement.
>>
> Même en faisant coller les codes de votre démo plus près de ma
> situation (ci-dessous), cela se déroule comme comme vous le dites. Et
> pendant près d'un an cela a fonctionné je ne sais comment. Je suis
> éloigné de mes bases toute cette semaine et le début de l'autre. Je
> vous remercie beaucoup et vais essayer de comprendre (ou de
> m'habituer) à ces comportements.

Une solution pourrait être que chaque bibliothèque utilise dlopen pour
charger les sous-bibliothèques qu'elle utilise elle même. Mais c'est
de la programmation C. Je trouverais mieux de le faire en lisp. Note
qu'il suffit de _définir_ un call-out ou une variable, pour que la
bibliothèque spécifiée par :LIBRARY soit chargée. Il n'est pas
nécessaire d'appeler la fonction définie.

On peut donc définir au niveau lisp une dépendance d'une bibliothèque
par la seule donnée d'une paire: nom de la bibliothèque, nom d'une
fonction dans la bibliothèque.

(define-foreign-library bib1 "libbib1.so" "bib1_init"
:depends-on (bib0))

(define-foreign-library bib2 "libbib2.so" "bib2_init"
:depends-on (bib1 bib3))

(define-foreign-library bib3 "libbib3.so" "bib3_init"
:depends-on (bib0))

(define-foreign-library example "libexample.so" "example_init"
:depends-on (bib1 bib2))

et alors:

(load-foreign-library example)

chargerait automatiquement les dépendances en exécutant simplement des
formes:

`(ffi:def-call-out ,(gensym)
(:name ,fun-name)
(:arguments) ; no need, since we won't call it.
(:return-type nil) ; idem
(:language :stdc)
(:library ,lib-name))

dans le bon ordre.


(On pourrait aussi éviter de nommer les bibliothèques avec des
symboles, en utilisant directement le nom "libexample.so", mais comme
ce nom peut changer d'une plateforme à l'autre (eg. "example.dll" sur
MS-Windows), ajouter une indirection permet de d'utiliser
load-foreign-library de façon un peu plus "portable").

--
__Pascal Bourguignon__ http://www.informatimago.com/

"What is this talk of "release"? Klingons do not make software
"releases". Our software "escapes" leaving a bloody trail of
designers and quality assurance people in its wake."

card.lemoine

unread,
Apr 24, 2008, 4:49:15 PM4/24/08
to
Pascal Bourguignon wrote:

> "card.lemoine" <card.l...@wanadoo.fr> writes:
>
>> Pascal Bourguignon a écrit :
>>> "card.lemoine" <st_ge...@tele.fr> writes:
>>>
>>>> Pascal Bourguignon a écrit :
>>>>
>>>>> Est-on sur que la libgsl est déjà chargée dans clisp?
>>>>>
>>>> Pardon je crois que j'ai compris votre question de travers.
>>>> Qu'entendez-vous par "déjà chargée" ?
>>> Les dépendances d'une bibliothèque ne sont pas chargées
>>> automatiquement. Il faut les charger explicitement.
>>>
>> Même en faisant coller les codes de votre démo plus près de ma
>> situation (ci-dessous), cela se déroule comme comme vous le dites. Et
>> pendant près d'un an cela a fonctionné je ne sais comment. Je suis
>> éloigné de mes bases toute cette semaine et le début de l'autre. Je
>> vous remercie beaucoup et vais essayer de comprendre (ou de
>> m'habituer) à ces comportements.
>
> Une solution pourrait être que chaque bibliothèque utilise dlopen pour
> charger les sous-bibliothèques qu'elle utilise elle même. Mais c'est
> de la programmation C. Je trouverais mieux de le faire en lisp. Note
> qu'il suffit de _définir_ un call-out ou une variable, pour que la
> bibliothèque spécifiée par :LIBRARY soit chargée. Il n'est pas
> nécessaire d'appeler la fonction définie.
>

Pas choquant s'il n'y en a qu'une. Laborieux et esthétiquement détestable si
ça doit devenir un "asdf" à part entière, je trouve.

Mais alors question : modules versus ffi (outre ce qui en est dit dans le
chapitre 31 (ou 32 ?) de l'"implementation notes" de Clisp ? Pour le coup
c'est la mise en oeuvre qui est laborieuse, mais c'est autre chose.

AMn

Pascal Bourguignon

unread,
Apr 24, 2008, 5:17:19 PM4/24/08
to card.l...@wanadoo.fr
"card.lemoine" <st_ge...@tele2.fr> writes:
> Pas choquant s'il n'y en a qu'une. Laborieux et esthétiquement détestable si
> ça doit devenir un "asdf" à part entière, je trouve.
>
> Mais alors question : modules versus ffi (outre ce qui en est dit dans le
> chapitre 31 (ou 32 ?) de l'"implementation notes" de Clisp ? Pour le coup
> c'est la mise en oeuvre qui est laborieuse, mais c'est autre chose.

C'est vite vu: les modules nécessitent une recompilation de clisp!

Et de toutes façons faire un module utilisant des bibliothèques
dynamiques demanderait autant de travail pour charger ces
bibliothèques.

--
__Pascal Bourguignon__ http://www.informatimago.com/

READ THIS BEFORE OPENING PACKAGE: According to certain suggested
versions of the Grand Unified Theory, the primary particles
constituting this product may decay to nothingness within the next
four hundred million years.

Pascal Bourguignon

unread,
Apr 26, 2008, 4:25:08 PM4/26/08
to card.l...@aliceadsl.fr
"card.l...@aliceadsl.fr" <card.l...@aliceadsl.fr> writes:
> Je vous soumets un problème posé par le comportement nouveau du programme
> que je développe depuis des mois mais sur lequel je tourne en rond depuis
> trois jours.
> Il s'agit d'un développement parallèle en C et en lisp sur une question de
> traitement du signal. Il s'agirait de deux développements séparés à ceci
> près : l'utilisation d'une bibliothèque commune à l'un et à l'autre, et qui
> se développe en même temps que les deux "mains".


Bon, alors après une étude un peu plus aprofondie du problème, voici
quelques explications et la solution.

dlopen(3) effectivement est capable de charger automatiquement les
dépendences des bibliothèques chargées dynamiquement. Mais il faut
savoir que:

- la bibliothèque doit être créée en spécifiant explicitement ses
dépendances. Il est en effet possible de la créer sans lister
les bibliothèques dynamiques dont elle dépend, et alors dlopen ne
saura pas qu'il faut les charger.

- dlopen va normalement chercher les bibliothèques dynamiques là où
elles sont installée (et telles que prises en compte par ldconfig).

- mais bien sur, on peut spécifier des chemins particuliers avec la
variable d'environnement LD_LIBRARY_PATH.

- CEPENDANT, la valeur de la variable d'environnement
LD_LIBRARY_PATH n'est prise en compte lors de l'initialisation de
libdl, la bibliothèque contenant dlopen, et non pas lorsque
dlopen est appelée. Ainsi changer la variable d'environnement au
cours de l'exécution du programme appelant dlopen n'est pas
effectif, il faut qu'elle soit définie avant de lancer le
programme.

En conclusion, pour résoudre ton problème, il faut compiler les
bibliothèques en prennant soin de lister leurs dépendances, et il faut
lancuer clisp en spécifiant un LD_LIBRARY_PATH incluant le chemin où
trouver ces bibliothèques, tant qu'elles ne sont pas installée aux
endroits normaux.

Lancer clisp avec:

LD_LIBRARY_PATH=$HOME/projet_C/lib:$HOME/lib:$LD_LIBRARY_PATH clisp

ou:

export LD_LIBRARY_PATH=$HOME/projet_C/lib:$HOME/lib:$LD_LIBRARY_PATH
clisp

Voir les détails dans :
http://www.informatimago.com/develop/lisp/two-libs.tar.gz

card.lemoine

unread,
Apr 28, 2008, 3:39:37 AM4/28/08
to
Voila sans doute pourquoi, sans avoir conscience du pb, j'ai pu faire
fonctionner le systus pendant près d'un an, jusqu'à ce que je réorganise
les répertoires.
Nouvelles dans quelques jours. Honteux de ne pouvoir être plus présent
sur une question que j'ai soulevé.
AMn
0 new messages