[LINUX] Limiter les symboles publics dans les bibliothèques partagées

introduction

Lors du développement d'une bibliothèque partagée, Vous souhaiterez limiter les symboles publics pour éviter toute utilisation abusive et améliorer les performances.

Si vous le définissez simplement sur la portée du fichier, vous ne pouvez pas l'appeler à partir d'un autre fichier, même dans la bibliothèque. De plus, dans le cas du C ++, même s'il s'agit d'une méthode privée, elle sera exposée à l'extérieur sous forme de symbole.

Par conséquent, je présenterai trois méthodes pour contrôler les symboles exposés à l'extérieur de la bibliothèque partagée.

Compte tenu des coûts de maintenance et de la prévention des fuites, [Méthode 3](https://qiita.com/takeoverjp/items/d86fba2843faac955d4b#%E6%96%B9%E6%B3%953-%E3%83%87% E3% 83% 95% E3% 82% A9% E3% 83% AB% E3% 83% 88% E3% 82% 92% E9% 9D% 9E% E5% 85% AC% E9% 96% 8B% E3% 81% AB% E3% 81% 97% E3% 81% 9F% E4% B8% 8A% E3% 81% A7% E5% 85% AC% E9% 96% 8B% E9% 96% A2% E6% 95% B0% E3% 81% AB__visibility__default% E3% 82% 92% E6% 8C% 87% E5% AE% 9A% E3% 81% 99% E3% 82% 8B) est la meilleure méthode.

Nous avons confirmé l'opération dans l'environnement suivant.

$ uname -m
x86_64
$ uname -r
5.5.8
$ lsb_release -d
Description:    Ubuntu 18.04.5 LTS
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) 

thème

Le «MyClass» suivant est défini dans la bibliothèque partagée, et ce qui suit sont des exigences.

・ Le constructeur / destructeur et la méthode publique sont ouverts au public ・ La méthode privée est privée

sample.h


class MyClass {
 public:
  MyClass();
  ~MyClass();

  void PublicMethod();
  int PublicMethodWithArgs(int argc, char* argv[]);

 private:
  void PrivateMethod();
  int PrivateMethodWithArgs(int argc, char* argv[]);
};

Les symboles publics, lorsqu'ils sont construits tels quels, sont les suivants. Vous pouvez voir que la méthode privée est également publiée.

$ g++ -W -Werror -shared -fPIC -o libsample.so sample.cc
$ nm -gC libsample.so | awk '$2=="T"' | grep MyClass
0000000000000722 T MyClass::PublicMethod()
0000000000000740 T MyClass::PrivateMethod()
000000000000072e T MyClass::PublicMethodWithArgs(int, char**)
000000000000074c T MyClass::PrivateMethodWithArgs(int, char**)
000000000000070a T MyClass::MyClass()
000000000000070a T MyClass::MyClass()
0000000000000716 T MyClass::~MyClass()
0000000000000716 T MyClass::~MyClass()

Méthode 1. Listez les fonctions publiques à l'aide du script de version LD

Vous pouvez limiter les symboles exposés en spécifiant le script de version pour ld. Les symboles décrits dans global: seront publiés, et les symboles décrits dans local: seront privés. Vous pouvez utiliser les caractères génériques «*» et «?».

Dans le cas de C ++, il est déformé, alors mettez-le dans extern" C ++ " Et vous devez spécifier le caractère générique «*» après le nom de la méthode. En conséquence, il peut être appliqué à des méthodes involontaires.

Au fait, la ligne MyClass ::? MyClass * sert à exposer le destructeur. Puisque ~ n'est pas valide dans la syntaxe du script de version, le caractère générique ? Est utilisé à la place. Cela peut également s'appliquer à des méthodes involontaires.

sample.map


{
  global:
    extern "C++" {
      MyClass::MyClass*;
      MyClass::?MyClass*;
      MyClass::PublicMethod*;
    };
  local:
    *;
};

Le résultat de la construction avec le script de version ci-dessus spécifié et de la vérification des symboles est le suivant. La méthode privée est privée.

$ g++ -W -Werror -shared -fPIC -Wl,--version-script=sample.map -o libsample.so sample.cc
$ nm -gC libsample.so | awk '$2=="T"' | grep MyClass
00000000000005e2 T MyClass::PublicMethod()
00000000000005ee T MyClass::PublicMethodWithArgs(int, char**)
00000000000005ca T MyClass::MyClass()
00000000000005ca T MyClass::MyClass()
00000000000005d6 T MyClass::~MyClass()
00000000000005d6 T MyClass::~MyClass()

Méthode 2: Spécifiez __visibility__ (" hidden ") pour une fonction privée

Public / private peut être spécifié comme attribut pour chaque fonction dans gcc.

Puisque la valeur par défaut est publique, pour la fonction que vous souhaitez garder privée comme ci-dessous Vous pouvez atteindre votre objectif en spécifiant __visibility__ (" hidden ").

sample.cc


MyClass::MyClass() {}

MyClass::~MyClass() {}

void
MyClass::PublicMethod() {}

int
MyClass::PublicMethodWithArgs(int argc, char* argv[]) {}

void __attribute__((__visibility__("hidden")))
MyClass::PrivateMethod() {}

int __attribute__((__visibility__("hidden")))
MyClass::PrivateMethodWithArgs(int argc, char* argv[]) {}

Même si vous ne spécifiez pas le script de version comme suit La méthode privée peut rester privée.

$ g++ -W -Werror -shared -fPIC -o libsample.so sample.cc
$ nm -gC libsample.so | awk '$2=="T"' | grep MyClass
0000000000000692 T MyClass::PublicMethod()
000000000000069e T MyClass::PublicMethodWithArgs(int, char**)
000000000000067a T MyClass::MyClass()
000000000000067a T MyClass::MyClass()
0000000000000686 T MyClass::~MyClass()
0000000000000686 T MyClass::~MyClass()

Méthode 3: Rendre la valeur par défaut privée et spécifier __visibility__ (" default ") dans la fonction publique

Je peux faire ce que je veux faire avec la méthode 2, mais avec la méthode de la liste noire, il y a certainement des fuites. (Parce qu'il peut être utilisé même s'il fuit)

Par conséquent, après avoir rendu la valeur par défaut privée, Je pense qu'il est sûr de spécifier les fonctions publiques en utilisant l'attribut d'une manière de liste blanche.

Pour garder la valeur par défaut privée, spécifiez -fvisibility = hidden au moment de la compilation. Après cela, contrairement au plan 2, spécifiez __visibility__ (" default ") pour la fonction que vous souhaitez exposer.

sample.cc


__attribute__((__visibility__("default")))
MyClass::MyClass() {}

__attribute__((__visibility__("default")))
MyClass::~MyClass() {}

void __attribute__((__visibility__("default")))
MyClass::PublicMethod() {}

int __attribute__((__visibility__("default")))
MyClass::PublicMethodWithArgs(int argc, char* argv[]) {}

void
MyClass::PrivateMethod() {}

int
MyClass::PrivateMethodWithArgs(int argc, char* argv[]) {}

La méthode privée peut être rendue privée comme suit.

$ g++ -W -Werror -shared -fPIC -fvisibility=hidden -o libsample.so sample.cc
$ nm -gC libsample.so | awk '$2=="T"' | grep MyClass
0000000000000692 T MyClass::PublicMethod()
000000000000069e T MyClass::PublicMethodWithArgs(int, char**)
000000000000067a T MyClass::MyClass()
000000000000067a T MyClass::MyClass()
0000000000000686 T MyClass::~MyClass()
0000000000000686 T MyClass::~MyClass()

Code expérimental

Il est placé en dessous.

takeoverjp/hide_shared_lib_symbols

référence

GNU Gnulib: Exported Symbols of Shared Libraries Visibility - GCC Wiki How To Write Shared Libraries GNU Gnulib: LD Version Scripts Using the GNU Compiler Collection (GCC): Common Function Attributes

Recommended Posts

Limiter les symboles publics dans les bibliothèques partagées
Utiliser la mémoire partagée avec une bibliothèque partagée
erreur lors du chargement des bibliothèques partagées
Déboguer les bibliothèques partagées avec VScode