Liaison dynamique - Linux Vs. les fenêtres

10

Sous Windows, lorsque je compile du code C / C ++ dans un projet DLL dans MSVC, j'obtiens 2 fichiers:

  1. MyDll.dll
  2. MyDll.lib

où, pour autant que je comprends, MyDll.libcontient une sorte de tableau de pointeurs indiquant les emplacements des fonctions dans la DLL. Lorsque vous utilisez cette DLL, par exemple dans un fichier exe, elle MyDll.libest intégrée dans le fichier exe pendant la liaison, de sorte qu'en cours d'exécution, elle "sait" où se trouvent les fonctions MyDll.dllet peut les utiliser.

Mais si je compile le même code sous Linux, je ne reçois qu'un seul fichier MySo.sosans MySo.a(l'équivalent d'un libfichier sous Linux), alors comment un fichier exécutable sous Linux sait-il où se trouvent les fonctions MySo.sosi rien n'y est intégré pendant la liaison?

Benny K
la source

Réponses:

1

Sous Linux, l'éditeur de liens (et non l'éditeur de liens dynamique) effectue une recherche dans les bibliothèques partagées spécifiées au moment du lien et crée des références à celles-ci dans l'exécutable. Lorsque l'éditeur de liens dynamique charge ces exécutables, il charge les bibliothèques partagées dont ils ont besoin dans la mémoire et résout les symboles, ce qui permet d'exécuter les binaires.

MySo.a, s'il était créé, inclurait en fait les symboles à lier directement dans le binaire au lieu des "tables de recherche de symboles" utilisées sous Windows.

la réponse de rustyx explique le processus sous Windows de manière plus approfondie que moi; cela fait longtemps que je n'ai pas utilisé Windows.

SS Anne
la source
1
"Windows adopte une approche différente ... spécifiez au système d'exploitation exactement où les symboles sont dans la DLL" - cela contredit le wiki , qui dit que les noms de fonction sont toujours résolus (au démarrage ou au premier appel à la fonction de bibliothèque) même lorsque vous utiliser des ordinaux (sauf si la liaison d'adresse directe est utilisée, ce que personne ne ferait car cela oblige les utilisateurs de la bibliothèque à recompiler et redéployer leur code chaque fois que la bibliothèque change).
yugr
@yugr Supprimé cette partie, je tenais quand même aux pailles.
SS Anne
4

L'éditeur de liens MSVC peut lier des fichiers d'objets (.obj) et des bibliothèques d'objets (.lib) pour produire un .EXE ou un .DLL.

Pour établir un lien avec une DLL, le processus dans MSVC consiste à utiliser une soi-disant bibliothèque d'importation (.LIB) qui sert de lien entre les noms de fonction C et la table d'exportation de la DLL (dans une DLL, une fonction peut être exportée par nom ou par ordinal - ce dernier était souvent utilisé pour les API non documentées).

Cependant, dans la plupart des cas, la table d'exportation DLL contient tous les noms de fonction et donc la bibliothèque d'importation (.LIB) contient des informations largement redondantes (" fonction d'importation ABC -> fonction exportée ABC ", etc.).
Il est même possible de générer un .LIB à partir d'un .DLL existant.

Les éditeurs de liens sur d'autres plates-formes n'ont pas cette "fonctionnalité" et peuvent se lier directement aux bibliothèques dynamiques.

rustyx
la source
"Les éditeurs de liens sur d'autres plates-formes n'ont pas cette fonctionnalité" - il est cependant facile à implémenter (par exemple Implib.so le fait pour Linux) pour obtenir un chargement retardé et d'autres avantages.
yugr
@yugr: c'est pourquoi "fonctionnalité" est entre guillemets - ce n'est pas quelque chose que vous voulez généralement faire et c'est un travail supplémentaire que vous devez faire sur Windows.
Chris Dodd
1

La différence que vous voyez est davantage un détail d'implémentation - sous le capot, Linux et Windows fonctionnent de la même manière - vous codez appelle une fonction de stub qui est liée statiquement dans votre exécutable et ce stub charge ensuite DLL / shlib si nécessaire (en cas de retard chargement , sinon la bibliothèque est chargée au démarrage du programme) et (au premier appel) résout le symbole via GetProcAddress/ dlsym.

La seule différence est que sous Linux, ces fonctions de stub (appelées stubs PLT) sont générées dynamiquement lorsque vous liez votre application à une bibliothèque dynamique (la bibliothèque contient suffisamment d'informations pour les générer), tandis que sous Linux, elles sont plutôt générées lorsque la DLL elle-même est créé, dans un .libfichier séparé .

Les deux approches sont si similaires qu'il est en fait possible d'imiter les bibliothèques d'importation Windows sur Linux (voir le projet Implib.so ).

yugr
la source
0

Sous Linux, vous passez MySo.soà l'éditeur de liens et il est capable d'extraire uniquement ce qui est nécessaire pour la phase de liaison, en mettant une référence qui MySo.soest nécessaire au moment de l'exécution.

AProgrammer
la source
-3

.dllou .sosont des bibliothèques partagées (liées au moment de l'exécution), alors que .aet .libest une bibliothèque statique (liée au moment de la compilation). Ce n'est pas une différence entre Windows et Linux.

La différence est, comment sont-elles gérées. Remarque: la différence ne concerne que les douanes, comment sont-elles utilisées. Il ne serait pas trop difficile de faire des builds Linux sur Windows et vice versa, sauf que pratiquement personne ne fait ça.

Si nous utilisons une DLL, ou si nous appelons une fonction même à partir de notre propre binaire, il existe un moyen simple et clair. Par exemple, en C, nous voyons que:

int example(int x) {
  ...do_something...
}

int ret = example(42);

Cependant, au niveau asm, il pourrait y avoir de nombreuses différences. Par exemple, sur x86, un callopcode est exécuté et 42est donné sur la pile. Ou dans certains registres. Ou n'importe où. Personne ne sait qu'avant d'écrire la DLL , comment elle sera utilisée. Ou comment les projets voudront l'utiliser, possible écrit avec un compilateur (ou dans un langage!) Qui n'existe même pas maintenant (ou est-il inconnu pour les développeurs de la dll).

Par exemple, par défaut, C et Pascal placent les arguments (et récupèrent les valeurs de retour) de la pile - mais ils le font dans un ordre différent . Vous pouvez également échanger des arguments entre vos fonctions dans les registres par une optimisation - dépendante du compilateur -.

Comme vous le voyez correctement, la coutume de Windows est que la construction d'une DLL, nous créons également un minimum .a/ .libavec elle. Cette bibliothèque statique minimale n'est qu'un wrapper, les symboles (fonctions) de cette DLL sont atteints à travers elle. Cela permet d'effectuer les conversions d'appels de niveau asm requises.

Son avantage est la compatibilité. Son inconvénient est que si vous n'avez qu'un .dll, vous pouvez avoir du mal à comprendre comment ses fonctions doivent être appelées. Cela fait de l'utilisation des DLL une tâche de piratage, si le développeur de la DLL ne vous donne pas le.a . Ainsi, il sert principalement à des fins de fermeture, par exemple, il est donc plus facile d'obtenir de l'argent supplémentaire pour les SDK.

Son autre inconvénient est que même si vous utilisez une bibliothèque dynamique, vous devez compiler statiquement ce petit wrapper.

Sous Linux, l'interface binaire des DLL est standard et suit la convention C. Ainsi, aucun .an'est requis et il existe une compatibilité binaire entre les bibliothèques partagées, en échange, nous n'avons pas les avantages de Microsoft Custom.

peterh - Réintégrer Monica
la source
1
Veuillez fournir un lien de preuve que les fonctions de stub peuvent changer l'ordre des arguments. Je n'en ai jamais entendu parler auparavant et c'est difficile à croire, compte tenu de l'ampleur des performances.
2019
@yugr Une simple réorganisation des registres / piles n'est pas une surcharge de performances. Si vous utilisez des DLL compilées par msvc à partir de binaires compilés par msvc, alors il ne se passera évidemment pas trop, mais cela pourrait arriver.
peterh
1
Nous pourrions discuter de cela, mais au cas où vous auriez raison, il devrait être facile de fournir des liens de preuve que les fonctions de stub sont un traitement non trivial des arguments (et plus que des trampolines factices).
yugr
@yugr Les stubs ont accès aux signatures de fonction de la dll, ce qui rend le traitement non trivial trivial.
peterh
1
Je vous suggère seulement de compléter votre réponse avec quelques liens de preuve concernant ce que fait la bibliothèque d'importation (car certaines des revendications sont discutables).
yugr