Le sujet de cet article est le suivant.
Le contenu est une plongée approfondie dans cet article. Je vais résumer à nouveau à partir de l'endroit où qu'est-ce qu'une bibliothèque et qu'est-ce qu'une API publique
Le programme démarre avec la fonction main et réalise les fonctions du programme en utilisant les fonctions appelées par main et les données utilisées. Cela ne fonctionnera pas sans les fonctions à appeler depuis la fonction principale et les données utilisées, il y aura donc toujours une entité quelque part dans le programme.
Lorsque vous créez un programme de cette manière, il y a des cas où vous voulez soudainement couper les coins ronds. Vous devez utiliser exactement la même fonction à chaque fois. La bibliothèque ** est un mécanisme pour rassembler ces ** exactement les mêmes fonctions.
La bibliothèque s'associe à une bibliothèque appelée ** lien de bibliothèque ** ou ** lien ** lorsque le programme est compilé.
Afin de lier la bibliothèque, la bibliothèque doit effectuer les opérations suivantes.
Vous devez divulguer des informations sur ʻapllo () `et les coupes utilisées dans la figure. Tel est le thème principal de cette fois, ** L'information ouverte au public **. Si les informations de fonction sont résolues par cette liaison, le programme peut utiliser la bibliothèque. À ce stade, la méthode d'utilisation diffère selon le type de bibliothèque.
Il s'agit d'une bibliothèque dans un format que le programme importe tel quel au moment de la compilation. Puisqu'elle est incluse dans le programme au moment de la construction, la fonction existe déjà lorsque le programme est exécuté, vous pouvez donc utiliser le programme sans réfléchir **. Étant donné que toute la bibliothèque recto-verso est importée, ** la taille augmentera **.
Le programme se souvient uniquement des informations de la bibliothèque au moment de la compilation et les associe au fichier cible au moment de l'exécution. (Cela s'appelle ** bibliothèques de liaison **)
Dans le cas d'une bibliothèque partagée, le programme ne dispose que des informations de la bibliothèque, il est donc nécessaire de ** rendre la bibliothèque cible liable ** lorsque le programme s'exécute.
Ce mécanisme est [comme celui-ci](https://qiita.com/developer-kikikaikai/items/f6f87b2d1d7c3e14fb52#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3% 81% AAlinux% E3% 81% AE% E5% 8B% 95% E7% 9A% 84% E3% 83% A9% E3% 82% A4% E3% 83% 96% E3% 83% A9% E3% 83% AA% E3% 83% AA% E3% 83% B3% E3% 82% AF% E3% 81% AB% E9% 96% A2% E3% 81% 99% E3% 82% 8B% E4% BB% 95% E7% B5% 84% E3% 81% BF).
En termes simples, c'est OK si le chemin de la bibliothèque cible est généré par la commande ldd. Sinon, vous devez transmettre le chemin dans la variable de construction ou d'environnement de la bibliothèque. C'est OK si le côté droit de => est lié quelque part comme indiqué ci-dessous.
$ ldd /usr/bin/curl
linux-vdso.so.1 (0x00007ffe307ac000)
libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f85cce4e000)
...
libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f85cbb4b000)
libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f85cb6d3000)
...
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f85c6c95000)
2018/05/16 postscript
~~ J'ai utilisé l'article par erreur, et ~~ le nom officiel de la bibliothèque. Je voudrais profiter de cette occasion pour résumer.
Nom | Exemple | Aperçu |
---|---|---|
Bibliothèque statique | Linux xx.a, Windows xxx.lib | Bibliothèque au format intégré au programme |
Bibliothèque partagée | Linux xx.so, Windows xxx.dll | Bibliothèque liée au démarrage du programme |
Bibliothèque dynamique | Linux dlopen(), Windows LoadLibrary() | Bibliothèque à lier pendant l'exécution du programme |
xx.a, Cela ressemble à une bibliothèque dynamique car elle est intégrée de manière fluide dans divers programmes. Je fais souvent une erreur si je n'organise pas mon cerveau comme "Dynamique ... Oh, c'est un nom qui vient d'une manière différente d'être pris en compte." ... peut-être juste moi. Oui
J'ai fait un long mouvement. De là, c'est la production. J'ai dit quelque chose, mais quelles sont les informations qui sont divulguées à l'extérieur? En termes de programme, c'est ** avec lien externe **. (Osez utiliser l'expression ** information publique dans cet article **)
Pour le dire très grossièrement en langage C, ** les bases sont des fonctions et des variables ** qui ne sont pas définies statiquement. Notez que la façon de penser est légèrement différente entre C et C ++. ** Si la classe n'est pas statique, C ++ peut ne pas être en mesure de bien gérer les variables internes même si la méthode est statique **. C'est vrai. Autres ** Il est nécessaire de comprendre la différence entre statique et statique, qui n'est pas une information publique en C ++ **. Pour plus d'informations sur C ++, veuillez consulter ici. Cet article se concentrera sur C.
Le problème avec cette seule condition est que ** Toutes les fonctions utilisées dans les fichiers de la bibliothèque seront des informations publiques! **. Donc, après cela, nous vous montrerons comment vérifier les informations publiques et comment les limiter. Il chevauche partiellement cet article.
Vous pouvez vérifier les informations publiques avec la commande nm.
Par exemple:
Tout d'abord, vérifiez le programme lui-même.
$nm -D test
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main
U memcmp
U __printf_chk
U publisher_free
U publisher_new
U publisher_publish
U publisher_subscribe
U publisher_unsubscribe
U puts
U __stack_chk_fail
Les minuscules sont une fonction locale, les détails sont omis. Le problème est capital. ** U n'est pas résolu **, c'est-à-dire une fonction qui doit être liée à l'exécution en tant que bibliothèque partagée. Étant donné que publisher_XXX etc. sont des fonctions créées par vous-même, elles ne peuvent être utilisées que si elles sont publiées.
D'un autre côté, la bibliothèque liée ressemble à ceci
nm .libs/libpublisher.so
...
0000000000000f90 t dputil_list_pop
0000000000000f50 t dputil_list_pull
0000000000000f20 t dputil_list_push
0000000000000ee0 t dputil_lock
0000000000000f00 t dputil_unlock
...
U pthread_mutex_init@@GLIBC_2.2.5
U pthread_mutex_lock@@GLIBC_2.2.5
U pthread_mutex_unlock@@GLIBC_2.2.5
U __pthread_register_cancel@@GLIBC_2.3.3
U __pthread_unregister_cancel@@GLIBC_2.3.3
w __pthread_unwind_next@@GLIBC_2.3.3
...
00000000000009b0 T publisher_free
0000000000202090 b publisher_g
0000000000000a00 T publisher_new
0000000000000b20 T publisher_publish
0000000000000a90 T publisher_subscribe
0000000000000ae0 T publisher_unsubscribe
0000000000000910 t register_tm_clones
U __sigsetjmp@@GLIBC_2.2.5
U __stack_chk_fail@@GLIBC_2.4
0000000000202078 d __TMC_END__
** T est une information publique **. Il existe un publisher_xxx correctement. En d'autres termes, il n'y a pas de problème si libpublisher.so.0.0.0
est prêt à être lié à l'exécution. pthread_mutex_init etc. est ** U **, mais comme il s'agit d'une fonction standard, elle est liée normalement.
À propos, il est inclus dans dputil_xxx et libpublisher.so
dans la fonction locale ci-dessus, mais il s'agit en fait d'une fonction de bibliothèque statique.
$ nm .libs/libdputil.a
dp_util.o:
00000000000000b0 T dputil_list_pop
0000000000000070 T dputil_list_pull
0000000000000040 T dputil_list_push
0000000000000000 T dputil_lock
0000000000000020 T dputil_unlock
U _GLOBAL_OFFSET_TABLE_
U pthread_mutex_lock
U pthread_mutex_unlock
Le fait que ce ne soit pas ** U ** signifie également que libpublisher.so l'a pris en compte au moment de la compilation.
Vous pouvez également vider les informations détenues par le programme avec la commande ʻobjdump`. Puisque vous pouvez vérifier diverses informations autres que des liens, cela semble utile pour l'analyse si vous pouvez la maîtriser (= je pense que c'est proche de la lecture de l'assembleur)
objdump -t libpublisher.so.0.0.0
libpublisher.so.0.0.0: file format elf64-x86-64
SYMBOL TABLE:
...
0000000000000ee0 l F .text 0000000000000012 dputil_lock
...
0000000000000000 F *UND* 0000000000000000 free@@GLIBC_2.2.5
...
0000000000000ae0 g F .text 0000000000000032 publisher_unsubscribe
...
Si ** g est joint comme ceci, information publique **, ** l est local **, ** UND n'est pas résolu **. Veuillez utiliser le sens travers pour la signification des mots détaillés.
Identique à l'article précédent. ** Spécifiez la fonction à publier dans XXX.map et ajoutez -Wl, --version-script, libtimelog.map aux options de construction **.
LDFLAGS+=-Wl,--version-script,libtimelog.map
libtimelog.map
{
global:
timetestlog_init;
timetestlog_store_printf;
timetestlog_exit;
local: *;
};
C'est simple et facile à comprendre, mais il est difficile de créer un fichier de paramètres.
-fvisibility=hidden
Si vous spécifiez -fvisibility = hidden
, toutes les fonctions sont d'abord rendues privées.
Ensuite, ajoutez __attribute __ ((visibilité (" default ")))
uniquement à ce dont vous avez besoin et publiez-le! C'est une technique appelée.
C'est un moyen idéal pour ceux qui aiment contrôler avec des règles de codage.
Dans libpublisher.so utilisé lors de l'introduction de nm,
-fvisibility = hidden
spécifié, ʻint __attribute __ ((visibilité (" default "))) publisher_new (size_t contents_num)pour chaque fonction publique
Je l'ai spécifié et construit comme ça.
En conséquence, je suis curieux que l'API de libdputil.a soit T, mais je peux le limiter à une sensation agréable.
nm -D .libs/libpublisher.so.0.0.0
0000000000202098 B __bss_start
U calloc
w __cxa_finalize
0000000000001210 T dputil_list_pop
00000000000011d0 T dputil_list_pull
00000000000011a0 T dputil_list_push
0000000000001160 T dputil_lock
0000000000001180 T dputil_unlock
0000000000202098 D _edata
00000000002020c0 B _end
0000000000001224 T _fini
U free
w __gmon_start__
0000000000000a00 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U pthread_mutex_init
U pthread_mutex_lock
U pthread_mutex_unlock
U __pthread_register_cancel
U __pthread_unregister_cancel
w __pthread_unwind_next
0000000000000c10 T publisher_free
0000000000000c80 T publisher_new
0000000000000da0 T publisher_publish
0000000000000d10 T publisher_subscribe
0000000000000d60 T publisher_unsubscribe
U __sigsetjmp
U __stack_chk_fail
Je préfère ** - version-script ** car je n'ai pas besoin d'être public / privé lors de l'écriture de code. C'est facile pour moi de les écrire tous ensemble en conf.
20/05/2018 postscript J'aime aussi le fait que cela puisse être automatisé avec un script. J'ai créé un exemple de script qui recherche le répertoire d'inclusion et crée un script de version. La réponse à la macro n'est pas bonne, mais je pense qu'elle peut être utilisée telle quelle.
#!/bin/sh
output_conf_map() {
#{
# global:
# function_name;
# ...
# local: *;
#}
#only get function list from header file
HEADER_FUNC_LIST=`grep "[a-zA-Z](" -r $1 | grep -v "@brief" | grep -v "#define" | awk -F"(" '{print $1}' | awk -F " " '{print $NF}'`
#template
echo "{"
echo " global:"
#show all function
for data in $HEADER_FUNC_LIST
do
echo " $data;"
done
#template end
echo " local: *;"
echo "};"
}
INCLUDE_LIST=`find . -name include`
for inc_dir in $INCLUDE_LIST
do
echo $inc_dir
output_conf_map $inc_dir
done
D'abord des excuses. La raison de la compilation de cet article est Article sur les restrictions de publication de la bibliothèque A été postée et commentée, une question et une réponse Zen telles que "Pourquoi existe-t-il différentes formes de moyens de restriction des API publiques pour les bibliothèques partagées? Qu'est-ce que public en premier lieu? Pourquoi les gens vivent-ils?" C'était le début. Par conséquent, dans un premier temps, j'ai voulu répondre à la question avec précision dans la langue de la zone de programme.
Cependant, il était impossible de parler de ce domaine sans creuser profondément, et comme la question et la réponse Zen ont vraiment commencé, j'ai pensé à nouveau: "Quelle était la bibliothèque que je voulais organiser?" Grâce à cela, je sens que la partie moelleuse a été effacée, qu'elle soit comprise ou non en tant que développeur middleware supérieur.
Après cela, j'ai beaucoup de mal à la bibliothèque, mais si je sais comment le vérifier, mon anxiété diminuera. Surtout autour d'OSS. ldd, nm super pratique
Un site qui explique comment gérer la mémoire avec beaucoup de soin. Kane: "Oh, c'est celui auquel je suis accro si je creuse plus profondément" Lorsqu'on lui a demandé la taille du code|Des choses que tu ne peux pas dire à l'école| [Collection de colonnes techniques]Portail intégré|Uquest Co., Ltd.
Gestion des fonctions vues de l'assembleur Une histoire d'être facilement vaincu avant la technologie récente en essayant de montrer d'anciennes techniques | Possible Eria
Définition du lien externe https://msdn.microsoft.com/ja-jp/library/k8w8btzz.aspx
Comment limiter le partage de bibliothèque en C ++ Comment écrire une bibliothèque partagée au format C ++ (édition gcc) --Qiita
Comment lire nm Afficher la liste des symboles de l'objet avec la commande nm --Qiita
Nom de la bibliothèque [Bibliothèque - Connaissance de base des termes de communication](http://www.wdic.org/w/TECH/ Library)