web-dev-qa-db-fra.com

ld linker question: l'option --whole-archive

La seule utilisation réelle de l’éditeur de liens --whole-archive que j’ai vue est de créer des bibliothèques partagées à partir de bibliothèques statiques. Je suis récemment tombé sur un ou plusieurs Makefile (s) qui utilisent toujours cette option lors de la liaison avec des bibliothèques statiques internes. Bien entendu, cela entraîne que les exécutables extraient inutilement du code objet non référencé. Ma réaction à cela a été que c'est tout simplement faux, est-ce que je manque quelque chose ici? 

La deuxième question que j’ai à faire concerne quelque chose que j’ai lu concernant l’option d’archive intégrale, mais que j’ai pu analyser. Quelque chose à l'effet que l'option --whole-archive devrait être utilisée lors de la liaison avec une bibliothèque statique si l'exécutable est également lié à une bibliothèque partagée qui possède (en partie) le même code objet que la bibliothèque statique. C'est-à-dire que la bibliothèque partagée et la bibliothèque statique se chevauchent en termes de code objet. L'utilisation de cette option forcerait tous les symboles (indépendamment de leur utilisation) à être résolus dans l'exécutable. Ceci est supposé éviter la duplication de code objet. Ceci est source de confusion. Si un symbole est référencé dans le programme, il doit être résolu uniquement au moment de la liaison. En quoi consiste le problème de la duplication? (Pardonnez-moi si ce paragraphe n’est pas tout à fait le meilleur de la clarté)

Merci 

32
jasmeet

Il existe des utilisations légitimes de --whole-archive lors de la liaison d'un exécutable avec des bibliothèques statiques. Un exemple est la construction de code C++, où les instances globales "s'enregistrent" dans leurs constructeurs (avertissement: code non testé):

main.cc

typedef void (*handler)(const char *protocol);
typedef map<const char *, handler> M;
M m;

void register_handler(const char *protocol, handler) {
   m[protocol] = handler;
}
int main(int argc, char *argv[])
{
   for (int i = 1; i < argc-1; i+= 2) {
      M::iterator it = m.find(argv[i]);
      if (it != m.end()) it.second(argv[i+1]);
   }
}

http.cc (partie de libhttp.a)

class HttpHandler {
  HttpHandler() { register_handler("http", &handle_http); }
  static void handle_http(const char *) { /* whatever */ }
};
HttpHandler h; // registers itself with main!

Notez qu'il n'y a pas de symboles dans http.cc dont main.cc a besoin. Si vous liez ceci comme

g++ main.cc -lhttp

vous pas aurez un gestionnaire http lié à l'exécutable principal et ne pourrez pas appeler handle_http(). Comparez cela à ce qui se passe lorsque vous créez un lien en tant que:

g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive

Le même style "d’auto-inscription" est également possible dans plain-C, par exemple. avec l'extension __attribute__((constructor)) GNU.

57

Une autre utilisation légitime de --whole-archive est que les développeurs de toolkit distribuent des bibliothèques contenant plusieurs fonctionnalités dans une seule bibliothèque statique. Dans ce cas, le fournisseur n'a aucune idée des parties de la bibliothèque qui seront utilisées par le consommateur et doit donc tout inclure. 

9
Steve Brooks

Je conviens que l’utilisation de —whole-archive pour créer des exécutables n’est probablement pas ce que vous voulez (en raison de la liaison dans du code inutile et de la création de logiciels volumineux). S'ils avaient une bonne raison de le faire, ils auraient dû le documenter dans le système de construction, car il ne reste plus qu'à deviner.

En ce qui concerne votre deuxième partie de la question. Si un exécutable lie à la fois une bibliothèque statique et une bibliothèque dynamique qui a (en partie) le même code d’objet que la bibliothèque statique alors le —whole-archive garantira qu’à l’heure de la liaison le code de la bibliothèque statique est préférable. C’est généralement ce que vous voulez quand vous faites des liens statiques.

4
lothar

Ancienne requête, mais pour votre première question ("Pourquoi"), j'ai vu - une archive unique utilisée également pour les bibliothèques internes, principalement pour contourner les références circulaires entre ces bibliothèques. Cela a tendance à masquer une architecture médiocre des bibliothèques, je ne le recommanderais donc pas. Cependant, c'est un moyen rapide d'obtenir un essai rapide.

Pour votre deuxième requête, si le même symbole était présent dans un objet partagé et une bibliothèque statique, l'éditeur de liens satisfera la référence avec la bibliothèque qu'il rencontre en premier.
Si la bibliothèque partagée et la bibliothèque statique ont un partage de code exact, tout cela peut fonctionner. Mais lorsque la bibliothèque partagée et la bibliothèque statique ont des implémentations différentes des mêmes symboles, votre programme sera toujours compilé mais se comportera différemment en fonction de l'ordre des bibliothèques.

Forcer tous les symboles à être chargés à partir de la bibliothèque statique est un moyen d’éliminer toute confusion quant à ce qui est chargé d’où. Mais en général, cela ressemble à résoudre le mauvais problème; vous ne voudrez surtout pas les mêmes symboles dans différentes bibliothèques.

3
Rob Swarbrick

Un autre bon scénario dans lequel --whole-archive est bien utilisé concerne les liaisons statiques et incrémentielles entre bibliothèques.

Supposons que:

  1. libA implémente les fonctions a() et b().
  2. Une partie du programme doit être liée à libA uniquement, par exemple. en raison d'un wrapping de fonction utilisant --wrap (un exemple classique est malloc)
  3. libC implémente les fonctions c() et utilise a()
  4. le programme final utilise a() et c()

Les étapes de liaison incrémentielles pourraient être:

ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA
ld -r -o step2.o step1.o module2.o --whole-archive -lC
cc step3.o module3.o -o program

Ne pas insérer --whole-archive effacerait la fonction c() qui est utilisée de toute façon par program, empêchant ainsi le processus de compilation correct.

Bien sûr, il s'agit d'un cas particulier dans lequel une liaison incrémentielle doit être effectuée pour éviter d'encapsuler tous les appels à malloc dans tous les modules, mais il s'agit d'un cas pris en charge avec succès par --whole-archive.

0
ilpelle