OVH Cloud OVH Cloud

Utilité DLL C++

102 réponses
Avatar
Korchkidu
Bonjour,

A partir de quand estimez vous que faire une DLL plutot qu'un LIB soit
int=E9ressant. En effet, utiliser des DLL fait des programmes plus
petits (entre autres) mais induit d'enormes contraintes au niveau de la
programmation. Donc d'apres moi, faire une DLL pour une bibliotheque de
petite taille n'est pas forcement une bonne idee...

Merci pour vos commentaires.
K=2E

10 réponses

Avatar
Jean-Marc Bourguet
Alain Gaillard writes:



Cela fait longtemps que j'ai fait de l'assembleur avec la
syntaxe MS -- mais il me semble que mov eax,dword ptr [i (10017168h)]
c'est de l'adressage absolu:


Je reviens sur la question. Donc oui ici.

donc le code sait qu'i est à
l'adresse 10017168h et pas ailleurs.


Mais non là.
Je pense que le mode d'adressage virtuel des Intel 32 bits
t'échappe


Je crois que tu me sous-estimes. J'ai fait pas mal
d'assembleur x86 dont un DOS extender, qui utilisait la
mémoire virtuelle pour imiter la mémoire paginée (donc je
partais du DOS, j'activais le mode protégé, j'activais la
pagination et je retournais sous DOS en mode virtuel, et
j'avais implémenté les interuptions LM? pour modifier le
mapping), je doute que ça ait fonctionné si je n'avais pas
compris les différents mode de fonctionnement de la machine.
A ma connaissance il n'y a pas eu de changement depuis cette
époque si on excepte le mode 64 bits.

Voilà pourquoi une adresse virtuelle donnée va pointer
vers des adresses physiques différentes selon le
processus. Quand Windows charge un exe il construit cette
table (table qui n'est pas la table de saut en mémoire
haute dont j'ai parlé avant, ça tombe sous le sens), donc
à priori différentes pour chaque processus, même si elle
peuvent avoir des points communs, comme le code partagé
des dll.


Bon, on va faire un exemple simple:
J'ai deux DLL: LIB1 et LIB2 et trois exécutables A, B, et C.
A utilise LIB1. B utilise LIB2. C utilise LIB1 et LIB2.

Je lance A, Windows mappe LIB1 à l'adresse virtuelle
$80000000 de A.

Je lance B, Windows mappe LIB2 à l'adresse virtuelle
$80000000 de B.

Je lance C, Windows ne peut pas mapper et LIB1 et LIB2 à
l'adresse virtuelle $80000000 de C, donc comme il y a des
références absolues il doit modifier le code et ne plus le
partager.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Alain Gaillard


On répète. Le problème n'a rien à voir avec les adresses
physiques en mémoire réele. C'est un problème de l'adresse dans
l'espace d'adressage du processus. Une fois que la variable
positionnée bien à 0x100217C0, comment est-ce que tu peux
garantir que cette adresse ne sert pas déjà dans d'autres
processus qui veulent utiliser la DLL ?


Mais en quoi est-ce un problème ?

--
Alain

Avatar
Vincent Burel
"Alain Gaillard" wrote in message
news:450586c2$0$25923$


Et alors. On le sait. (Je le sais très bien, et je suis sûr que
Jean-Marc le sait aussi.)

Oui, mais chaque processus accédera bien à la même adresse à
l'intérieur de son espace d'adressage.



Mais enfin, pourquoi voulez vous qu'une même adresse virtuelle pour tout
le monde corresponde à la même adresse physique pour tout le monde ? Ca
dépend de la table de descripteur propre à chaque processus et que le
système constitue losqu'il lance un exe.


pour un exe ca pose pas de probleme de le loger toujours au meme endroit ,
par exemple à l'adresse 0x00400000. Au contraire c'est plus simple... le
problemes se pose pour les DLL... et leur mappage dans l'espace d'adressage
du process. je crois que c'est ce dont parle Mr Kanze depuis le début en
fait :-)

VB


Avatar
Alain Gaillard

Nan ! J'aime pas le ton de certains intervenants de ce fil, je fais grève.



Dois je me sentir visé ?
Si oui, je te répète ce que j'ai dit juste au dessous. Je suis navré que
tu prennes tout ça comme ça.

Et si je t'ai tant paru "docte", je ne le voulais certainement pas.
Là, je suis la discussion en bossant, peut être que du coup je réponds
trop vite ou pas avec le ton qu'il faut, mais il n'y a pas de mauvaises
intentions chez moi.

--
Alain

Avatar
Arnold McDonald \(AMcD\)
kanze wrote:
Alain Gaillard wrote:

Mais ça ne change rien au problème. Que ce soit l'éditeur de
liens classiques qui fasse la rélocation, ou la fonction
LoadLibrary de l'OS, le fait reste qu'une fois l'image de la DLL
en mémoire, l'adresse doit être 0x10017168. Et que si cette
adresse est déjà prise lorsque le processus charge la DLL, il y
a un petit problème.


C'est pourtant pas compliqué. Il te faut étudier le principe de
fonctionnement des sections .reloc des exécutables. Cette section permet au
loader de connaître toutes les parties de l'exécutable à reloger. Si tu as
comme code :

mov eax,dword ptr [0x00100068]

Cela sera stipulé dans le reloc de ton exécutable. Après, c'est facile, le
loader ajoute l'adresse de chargement de ton exécutable aux relocs. C'est le
même principe pour les DLLs. Si le code est basé en 0x00500000 ben
l'instruction deviendra :

mov eax,dword ptr [0x00600068]

Note bien que le 0x0010068 de départ est une combinaison adresse de base
0x0010000 + RVA de la donnée à lire 0x00000068. Donc, pour simplifier, le
compilo génèrerait 100000 (disons que c'est une adresse de base générique) +
68 (l'offset RVA de la variable par rapport au code), le loader obtiendrait
0060068 (le mappage dans l'espace du processus en cours d'exécution).

Si tu veux rentrer dans les détails, il y a plusieurs types de relocation
et, bien évidemment, plusieurs méthodes de traitement. Cela dépend aussi de
si tu fais du 32 bits ou du 64 bits.

La base, c'est quand même cette structure :

typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;

Chaque donnée à reloger possède une entrée comme celle là dans le .reloc.
C'est pas donc dur pour le loader de calculer les relogements :-). Si le
sujet te passionne, je te conseille la lecture de quelques fichiers du DDK.
Au minimum ntimage.h et winnt.h. Au travers des différentes structures, tu
apprendras beaucoup de choses non expliquées dans le MSDN.

Un bon article qui présente pas mal de choses également :

http://msdn.microsoft.com/msdnmag/issues/02/08/EscapefromDLLHell/
http://msdn.microsoft.com/msdnmag/issues/02/06/debug/

--
Arnold McDonald (AMcD) - Help #54/2006

http://arnold.mcdonald.free.fr/

Avatar
Jean-Marc Bourguet
"Arnold McDonald (AMcD)" writes:

Jean-Marc Bourguet wrote:

Tu peux m'expliquer comment travailler avec un x86 en mode
32 bits sans que les adresses soient des déplacements par
rapport à des segments? (Ces segments généralement ont pour
base l'adresse 0 et une limite de 4GB, je veux bien mais ça
ne change rien, s'il n'y pas d'indication explicite de
segment, c'est DS qui est utilisé sauf quand on utilise des
déplacements par raport à (E)BP et (E)SP).


Les processeurs IA-32 possèdent 3 modèles de mémoire.


Nous sommes d'accord.

Le modèle dit "flat" t'autorises un adressage linéaire,
sans segment ni offset lorsque tu accèdes, par exemple à
un octet.


Il n'y a aucune référence à un mode flat dans la doc d'intel
pour les 386 et 486 (la dernière à laquelle j'ai eu accès).
Les 3 modes sont pour moi:
- le mode réel qui forme directement les adresses
physiques par combinaison de la valeur du segment et du
décalage. Ce mode comporte des choses amusantes, comme
le fait qu'on peut (pouvait? je ne sais pas si le hack
est toujours possibe) lui faire adresser 4GB (le truc est
de passer en mode protégé, charger des segments avec une
limite de 4GB et revenir, les limites étaient conservées
tant qu'on ne modifiait pas les sélecteurs).

- le mode protégé, ou les adresses virtuelles sont
formées en ajoutant un décalage à une base optenue dans
la table des segments. Cette adresse virtuelle est
alors être transformée en adresse physique via une autre
série de tables

- le mode virtuel, où des adresses *virtuelles* sont
formées comme les adresses physiques du mode réel et sont
par après transformée en adresse physique par la
pagination.

Le mode flat a toujours désigné pour moi l'utilisation d'un
segment recouvrant la totalité de la mémoire virtuelle en
mode protégé, mais n'a jamais empéché d'utiliser des
segments placés différemment (comme je l'ai déjà écrit, il
me semble que Windows utilise d'ailleurs un registre de
segment pour pointer vers la zone des variables locales à un
thread).


Depuis le P6 et les PAE, PSE et compagnie, tu peux
adresser des segments supérieurs à 4 GB.


Ou j'ai mal compris, ou c'est toi. Ces modes ne servent en
rien à étendre l'espace de la mémoire virtuelle mais au
contraire à étendre la mémoire physique. Autrement dit,
rien ne change dans la partie segmentation (qui forme les
adresses virtuelles) mais la partie pagination peut adresser
plus que 32 bits. Pour en profiter dans un processus, il
faudrait revenir à une technique semblable à la fameuse
mémoire paginée LIM -- j'ai retrouvé le troisième laron:
Intel --; peut-être que Windows le permet d'ailleurs.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Jean-Marc Bourguet
Alain Gaillard writes:

Bon, on va faire un exemple simple:
J'ai deux DLL: LIB1 et LIB2 et trois exécutables A, B, et C.
A utilise LIB1. B utilise LIB2. C utilise LIB1 et LIB2.
Je lance A, Windows mappe LIB1 à l'adresse virtuelle
$80000000 de A.
Je lance B, Windows mappe LIB2 à l'adresse virtuelle
$80000000 de B.
Je lance C, Windows ne peut pas mapper et LIB1 et LIB2 à
l'adresse virtuelle $80000000 de C, donc comme il y a des
références absolues il doit modifier le code et ne plus le
partager.


Ma foi je pense qu'il va en reloger une des deux
ailleurs. Windows reloge toujours le code qu'il charge à
partir d'une base de chargement. On peut même proposer une
base de chargement via l'éditeur de liens. Si ça convient
au système il l'applique, sinon il en choisit une autre.


Et ce qui est relogé ne peut pas être partagé, c'est cette
possibilité dont James parle depuis le début.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Jean-Marc Bourguet
"kanze" writes:

Dans l'ensemble, ça me semble un bon compromis. Avec un peu de
bol, il arrivera bien à réutiliser l'image la plupart du temps,
sans pour autant payer le coût (important) en temps d'exécution
qu'a la solution Linux, où les données sont « position
independant » aussi. (Dans le cas où la fonction a un
traitement important, évidemment, le temps supplémentaire
d'exécution n'est pas mesurable. Mais avec une petite fonction
comme ceci, le fait qu'elle soit dans un .so pourrait bien
doubler son temps d'exécution.)


Sous Linux tu n'es pas non plus obligé de compiler en PIC
pour mettre du code dans un .so.

Mais sous Linux (au moins la version que j'utilise pour le
moment), les adresses de chargement des .so varient, même si
on lance deux fois le même exécutable. Je crois que c'est
pour compliquer l'exploitation des buffers overflow.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Jean-Marc Bourguet
"kanze" writes:

Jean-Marc Bourguet wrote:
Je préviens (surtout qu'il y a un crosspost où peu de gens
doivent me connaîre) que je ne connais rien à Windows.

Alain Gaillard writes:



g++ -fpei sous Linux, par exemple). Cet offset, c'est par
rapport à DS.



Je suppose qu'il s'agit de -fpic et pas -fpei.


C'est ce que j'avais crû aussi, mais c'est bien -fpie (et non
-fpei, qui est effectivement une faute de frappe). C'est -pic
avec Sun CC.


Ça explique pourquoi je n'ai pas trouvé en cherchant dans la
doc. Linux utilise aussi pic (et à lire la doc de gcc,
-fpie n'est pas utilisable pour les .so)

Enfin, quelqu'un qui comprend ma question ? Je commençais à
devenir fou, ne sachant pas comment me rendre clair.


Je l'ai comprise depuis le début.

Tout est rélatif. J'ai déjà vu pas mal de processeurs
pires. Où les sauts étaient aux adresses absolues, et non
rélatifs au IP.


C'est vrai que c'est plus adapté qu'un PDP-10 :-)

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org




Avatar
Jean-Marc Bourguet
"Arnold McDonald (AMcD)" writes:

Jean-Marc Bourguet wrote:
"Arnold McDonald (AMcD)" writes:

Il n'y a aucune référence à un mode flat dans la doc d'intel
pour les 386 et 486 (la dernière à laquelle j'ai eu accès).


Il n'y en a aucune non plus dans les peintures
préhistoriques de la grotte de Gargas. On est en 2006 et
au processuer multi-coeur now hein...


Et alors, si tu as la moindre information qui fait penser
que ça a fondamentalement changé, je suis preneur d'une
référence. Parce que suite à ton message j'ai cherché un
peu et à lire la doc d'AMD -- celle d'Intel est mieux
cachée, je n'ai trouvé que du blabla techno-marketing, mais
je suis particulièrement mauvais pour chercher dans les
sites web -- les choses n'ont pas changé fondamentalement si
on exclus le mode 64 bits.

Depuis le P6 et les PAE, PSE et compagnie, tu peux
adresser des segments supérieurs à 4 GB.


Ou j'ai mal compris, ou c'est toi. Ces modes ne servent en
rien à étendre l'espace de la mémoire virtuelle mais au
contraire à étendre la mémoire physique. Autrement dit,
rien ne change dans la partie segmentation (qui forme les
adresses virtuelles) mais la partie pagination peut adresser
plus que 32 bits. Pour en profiter dans un processus, il
faudrait revenir à une technique semblable à la fameuse
mémoire paginée LIM -- j'ai retrouvé le troisième laron:
Intel --; peut-être que Windows le permet d'ailleurs.


J'ai pas le temps là, je détaillerai ce soir. On verra
bien qui a mal compris...


Il faudra des références: à lire la doc d'AMD (je parle de
la publication #24593 "AMD64 Architecture Programmer's
Manual Volume 2: System Programming"),

PSE: Page Size Extension: permet d'avoir des pages de 4M
plutôt que de 4K, mais toujours un espace virtuel de 32
bits.

PAE: Physical-Address Extension, le nom l'indique déjà assez
bien, ce sont les adresses physiques qui sont portées à 52
bits (J'ai l'impression que des processeurs plus vieux
supportait moins).

On revit juste l'évolution normale déjà plusieurs fois
vécue: au départ la mémoire virtuelle est supérieure à la
mémoire physique, on arrive à l'égalité puis on passe en
dessous...

Tiens, le premier hit dans google avec PAE et Windows me
donne:
http://www.microsoft.com/whdc/system/platform/server/PAE/pae_os.mspx
où je lis:

3. Application Windowing
A PAE-enabled operating system can introduce an API to allow
a properly coded application access to physical memory
anywhere in the system, even though it may be above 4 GB

Exactement ce que faisait les DOS extender pour simuler la
mémoire paginée LIM. Outre la mémoire paginée LIM, la
technique de fenêtre a été aussi utilisée sur les micros
tournant avec CPM et sur les systèmes avec des PDP-11. Une
techno datant au moins des années 70 en 2006 sur les
processeurs multic½urs :-)

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org