Je construis une bibliothèque partagée en C qui est chargée dynamiquement par un programme auquel je n'ai pas accès. La plate-forme cible est une plate-forme Linux 64 bits et nous utilisons gcc pour la construction. J'ai été capable de construire une reproduction du problème en ~ 100 lignes, mais c'est encore un peu à lire. J'espère que c'est illustratif.Pouvez-vous compiler un objet partagé pour qu'il préfère les symboles locaux même s'il est chargé par un programme compilé avec -rdynamic?
Le problème principal est que j'ai deux fonctions non statiques (bar
et baz
) définies dans ma bibliothèque partagée. Les deux doivent être non-statiques car nous nous attendons à ce que l'appelant puisse les désobéir. De plus, baz
appelle bar
. Le programme qui utilise ma bibliothèque a également une fonction nommée bar
, ce qui ne serait normalement pas un problème, mais le programme appelant est compilé avec -rdynamic
, car il a une fonction foo
qui doit être appelée dans ma bibliothèque partagée. Le résultat est que ma bibliothèque partagée finit par être liée à la version bar
du programme appelant à l'exécution, produisant des résultats non intuitifs.
Dans un monde idéal, je serais en mesure d'inclure un commutateur de ligne de commande lors de la compilation de ma bibliothèque partagée qui empêcherait que cela se produise.
La solution actuelle que j'ai est de renommer mes fonctions non-statiques comme funname_local
et de les déclarer statiques. Je définis ensuite une nouvelle fonction: funname() { return funname_local(); }
, et remplacez toute référence à funname
dans ma bibliothèque partagée par funname_local
. Cela fonctionne, mais cela semble lourd, et je préférerais simplement que l'éditeur de liens préfère les symboles définis dans l'unité de compilation locale.
internal.c
#include <stdio.h>
#include "internal.h"
void
bar(void)
{
printf("I should only be callable from the main program\n");
}
internal.h
#if !defined(__INTERNAL__)
#define __INTERNAL__
void
bar(void);
#endif /* defined(__INTERNAL__) */
main.c
#include <dlfcn.h>
#include <stdio.h>
#include "internal.h"
void
foo(void)
{
printf("It's important that I am callable from both main and from any .so "
"that we dlopen, that's why we compile with -rdynamic\n");
}
int
main()
{
void *handle;
void (*fun1)(void);
void (*fun2)(void);
char *error;
if(NULL == (handle = dlopen("./shared.so", RTLD_NOW))) { /* Open library */
fprintf(stderr, "dlopen: %s\n", dlerror());
return 1;
}
dlerror(); /* Clear any existing error */
*(void **)(&fun1) = dlsym(handle, "baz"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
*(void **)(&fun2) = dlsym(handle, "bar"); /* Get function pointer */
if(NULL != (error = dlerror())) {
fprintf(stderr, "dlsym: %s\n", error);
dlclose(handle);
return 1;
}
printf("main:\n");
foo();
bar();
fun1();
fun2();
dlclose(handle);
return 0;
}
main.h
#if !defined(__MAIN__)
#define __MAIN__
extern void
foo(void);
#endif /* defined(__MAIN__) */
shared.c
#include <stdio.h>
#include "main.h"
void
bar(void)
{
printf("bar:\n");
printf("It's important that I'm callable from a program that loads shared.so"
" as well as from other functions in shared.so\n");
}
void
baz(void)
{
printf("baz:\n");
foo();
bar();
return;
}
compilation:
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c
course:
$ ./main
main:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
baz:
It's important that I am callable from both main and from any .so that we dlopen, that's why we compile with -rdynamic
I should only be callable from the main program
bar:
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so
En savoir plus sur le (https://gcc.gnu.org/wiki/Visibility) [visibilité] attribut. C'est probablement pertinent. Considérez aussi '#define foo foo_internal'. BTW vous pourriez automatiser une partie de cela, peut-être en personnalisant votre compilateur (par exemple avec [GCC MELT] (http://gcc-melt.org/) ....) –
Notez que C n'autorise pas le même programme à contenir deux différentes entités avec un lien externe pour avoir le même nom, de sorte que vous courez dans un territoire douteux pour commencer. Bien que ELF * permette * une telle duplication de symboles, il n'y a aucun moyen de renommer le 'bar()' de la bibliothèque partagée en quelque chose d'unique, et d'ordonner au programme principal de le rechercher par ce nom (par exemple configuration du programme)? –
Vous pourriez envisager de lire [l'article d'Ulrich Drepper sur ELF] (https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf). Il a beaucoup à dire sur la résolution des symboles. Je ne sais pas si ce que vous voulez est vraiment possible, mais Drepper devrait vous aider à répondre à cette question. –