2016-09-13 1 views
3

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 
+0

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/) ....) –

+0

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)? –

+0

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. –

Répondre

5

Avez-vous essayé l'option de lien -Bsymbolic (ou -Bsymbolic-functions)? Je cite ld homme:

-Bsymbolic

Lors de la création d'une bibliothèque partagée, lier des références à des symboles globaux à la définition de la bibliothèque partagée, le cas échéant. Normalement, il est possible qu'un programme lié à une bibliothèque partagée remplace la définition dans la bibliothèque partagée. Cette option peut également être utilisée avec l'option --export-dynamic, lors de la création d'un exécutable indépendant de la position, pour lier les références aux symboles globaux à la définition dans l'exécutable. Cette option n'a de sens que sur les plateformes ELF qui supportent les bibliothèques partagées et les exécutables indépendants de la position.

Il semble résoudre le problème:

$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -o shared.so shared.c 
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -o main main.c internal.c -l dl -rdynamic 
$ ./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 
$ gcc -m64 -std=c89 -Wall -Wextra -Werror -pedantic -shared -fPIC -Wl,-Bsymbolic -o shared.so shared.c 
$ ./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 
bar: 
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so 
bar: 
It's important that I'm callable from a program that loads shared.so as well as from other functions in shared.so 
+0

C'est exactement ce que je cherchais. Merci! – TheGeneral

1

Une solution commune à ce problème est de ne pas en fait dépendre d'un symbole global ne pas être surchargé.faire au lieu de ce qui suit:

  • Appelez la fonction bar de votre bibliothèque mylib_bar ou quelque chose comme ça
  • Cacher mylib_bar avec __attribute__((visibility("hidden"))) ou similaire
  • Faire bar un symbole faible, se référant à mylib_bar comme ceci:

    #pragma weak bar = mylib_bar 
    
  • Appeler pour appeler votre bibliothèque mylib_bar partout au lieu de bar

Maintenant, tout fonctionne comme prévu:

  • Lorsque votre bibliothèque appelle mylib_bar, cela fait toujours référence à la définition dans la bibliothèque que la visibilité est cachée.
  • Lorsque d'autres appels de code bar, cela appelle mylib_bar par défaut.
  • Si quelqu'un définit son propre bar, cela remplace bar mais pas mylib_bar, laissant votre bibliothèque intacte.
+0

Bien qu'il utilise un mécanisme légèrement différent, cela semble aller dans le sens de l'approche que l'OP cherche à remplacer.Bien sûr, la méthode qu'il a déjà est celle que j'étais sur le point de lui recommander, alors peut-être que le message à retenir est que ces techniques ne sont pas si mauvaises après tout. –

+0

@JohnBollinger En effet. La principale différence est l'utilisation de symboles faibles et de visibilité pour forcer cette idée à fonctionner. – fuz

+1

@JohnBollinger le problème avec ma solution initiale au problème, c'est que tout finit par être déclaré statique. Si 'bar' est invoqué à partir de plusieurs fichiers source de différence à l'intérieur de mon objet partagé, nous avons des problèmes d'étendue de fichier. @FUZxxl a une meilleure solution car elle ne se casse pas si 'bar' est appelée depuis plusieurs portées de fichiers dans ma bibliothèque partagée. Cela dit, @ roman-khimov a une solution qui répond le mieux à ma demande initiale. – TheGeneral