43 votes

Suppression des bibliothèques partagées Linux

Nous avons récemment demandé d'expédier une version Linux de l'un de nos bibliothèques, auparavant, nous avons développé sous Linux et expédiés pour Windows où le déploiement des bibliothèques est généralement beaucoup plus facile. Le problème que nous avons trouvé est en dépouillant les symboles exportés vers le bas pour seulement ceux dans l'exposé de l'interface. Il y a trois bonnes raisons de vouloir le faire

  • Afin de protéger les droits de propriété de notre technologie de l'exposition à travers les symboles exportés.
  • Pour empêcher les utilisateurs ayant des problèmes de conflit noms de symbole.
  • Afin d'accélérer le chargement de la bibliothèque (au moins je me dit).

En prenant un exemple simple alors:

test.cpp

#include <cmath>

float private_function(float f)
{
    return std::abs(f);
}

extern "C" float public_function(float f)
{
    return private_function(f);
}

compilé avec g++ 4.3.2, ld 2.18.93.20081009)

g++ -shared -o libtest.so test.cpp -s

et d'inspecter les symboles avec

nm -DC libtest.so

donne

         w _Jv_RegisterClasses
0000047c T private_function(float)
000004ba W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
00000508 T _fini
00000358 T _init
0000049b T public_function

manifestement inadéquat. Donc nous redeclare la fonction publique

extern "C" float __attribute__ ((visibility ("default"))) 
    public_function(float f)

et compiler avec

g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden

ce qui donne

         w _Jv_RegisterClasses
0000047a W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004c8 T _fini
00000320 T _init
0000045b T public_function

ce qui est bon, sauf que std::abs est exposée. Plus problématique, c'est lorsque nous avons commencer à relier à d'autres (statique) des bibliothèques à l'extérieur de notre contrôle, tous les symboles que nous utilisons à partir de ces bibliothèques exportés. En outre, lorsque nous commençons à utiliser des conteneurs STL:

#include <vector>
struct private_struct
{
    float f;
};

void other_private_function()
{
    std::vector<private_struct> v;
}

nous nous retrouvons avec de nombreux autres exportations à partir de la bibliothèque C++

00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int)
00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator()
00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator()
00000ac4 W std::allocator<private_struct>::allocator()
00000a96 W std::allocator<private_struct>::~allocator()
00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl()
00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl()
00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int)
00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator()
00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base()
00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base()
00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector()
00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector()

NB: Avec des optimisations sur vous devrez vous assurer que le vecteur est effectivement utilisé de sorte que le compilateur n'a pas d'optimiser le solde non utilisé des symboles à partir.

Je crois que mon collègue a réussi à construire un groupe ad-hoc solution impliquant les fichiers de la version et de la modification de la STL en-têtes (!) qui semble fonctionner, mais je voudrais vous demander:

Est-il un moyen propre à la bande de tous les inutiles symboles (c'est à dire ceux qui ne font pas partie de l'exposé de la bibliothèque de la fonctionnalité) à partir d'une bibliothèque partagée? J'ai essayé tout un tas d'options à la fois g++ et ld avec peu de succès donc je préfère des réponses qui sont connus pour travailler plutôt que cru.

En particulier:

  • Les symboles de (closed-source) les bibliothèques statiques ne sont pas exportés.
  • Les symboles de la bibliothèque standard ne sont pas exportés.
  • Non-public des symboles à partir des fichiers de l'objet ne sont pas exportés.

Notre exporté interface est C.

Je suis conscient des autres questions similaires sur DONC:

mais ils ont eu peu de succès avec les réponses.

9voto

Adam Bowen Points 3633

Donc la solution que nous avons pour l'instant est comme suit:

test.cpp

#include <cmath>
#include <vector>
#include <typeinfo>

struct private_struct
{
    float f;
};

float private_function(float f)
{
    return std::abs(f);
}

void other_private_function()
{
    std::vector<private_struct> f(1);
}

extern "C" void __attribute__ ((visibility ("default"))) public_function2()
{
    other_private_function();
}

extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f)
{
    return private_function(f);
}

les exportations.version

LIBTEST 
{
global:
    public*;
local:
    *;
};

compilé avec

g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version

donne

00000000 A LIBTEST
         w _Jv_RegisterClasses
         U _Unwind_Resume
         U std::__throw_bad_alloc()
         U operator delete(void*)
         U operator new(unsigned int)
         w __cxa_finalize
         w __gmon_start__
         U __gxx_personality_v0
000005db T public_function1
00000676 T public_function2

Ce qui est assez proche de ce que nous recherchons. Il y a quelques pièges bien:

  • Nous devons nous assurer de ne pas utiliser la fonction "exporter" préfixe (dans cet exemple simple "public", mais de toute évidence quelque chose de plus utile dans notre cas) dans le code interne.
  • De nombreux noms de symbole même de la table de chaînes, qui semble être en panne de RTTI, -fno-rtti fait d'eux d'aller loin dans mes tests simples, mais est plutôt nucléaires solution.

Je suis heureux d'accepter toutes les meilleures solutions quelqu'un vient avec!

8voto

joshperry Points 17727

Votre utilisation de la visibilité par défaut de l'attribut et -fvisibility=cachés doivent être complétées par -fvisibility-inlines-caché.

Vous devez également oublier d'essayer de les cacher stdlib les exportations, voir ce bug de GCC pour pourquoi.

Aussi, si vous avez tous vos symboles dans les en-têtes spécifiques, vous pouvez les envelopper dans #pragma GCC visibility push(default) et #pragma GCC visibility pop au lieu d'utiliser les attributs. Mais si vous êtes à la création d'une plate-forme de bibliothèque, regardez le Contrôle de Symboles Exportés de Bibliothèques Partagées pour une technique d'unifier votre DLL de windows et de Linux ASM stratégie à l'exportation.

7voto

grrussel Points 3315

Juste pour noter qu'Ulrich Drepper a écrit un essai concernant (tous?) Les aspects de l' écriture de bibliothèques partagées pour Linux / Unix, qui couvre le contrôle des symboles exportés parmi de nombreux autres sujets.

C'était très pratique pour expliquer clairement comment exporter uniquement les fonctions d'une liste blanche à partir d'une bibliothèque partagée.

6voto

catwalk Points 3508

Si vous enveloppez votre partie privée dans un espace de noms anonyme, ni std::abs ni private_function ne peuvent être vus dans la table des symboles:

 namespace{
#include<cmath>
  float private_function(float f)
  {
    return std::abs(f);
  }
}
extern "C" float public_function(float f)
{
        return private_function(f);
}
 

compilation (g ++ 4.3.3):

g++ -shared -o libtest.so test.cpp -s

inspecter:

 # nm -DC libtest.so
         w _Jv_RegisterClasses
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004a8 T _fini
000002f4 T _init
00000445 T public_function
 

3voto

bmargulies Points 49855

En général, à travers de multiples systèmes Linux et Unix, ici, la réponse est qu'il n'y a pas de réponse ici, au moment de la liaison. c'est assez fondamental à la façon dont ld.donc il fonctionne.

Cela conduit à certains plutôt de main-d'oeuvre des solutions de rechange. Par exemple, nous renommer STL de vivre dans _STL au lieu de std afin d'éviter des conflits sur la STL, et nous utiliser des espaces de noms en haut, en bas, et entre les à garder nos symboles loin de conflits possibles avec d'autres personnes de symboles.

Voici une solution que vous n'aimerez pas:

  1. Créer une petite .donc, avec seulement votre exposé API il.
  2. Ouvrir la réelle mise en œuvre avec dlopen, et en lien avec dlsym.

Tant que vous n'utilisez pas RTLD_GLOBAL, vous avez maintenant une isolation complète si pas de secret .. -Bsymbolic pourrait également être souhaitable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X