Article de reference

Déformation de noms

( Apprenez comment et quand supprimer ce message ) Dans la construction de compilateurs , la modification des noms (également appelée décoration de noms ) est une technique util...

de compilateurs , la modification des noms (également appelée décoration de noms ) est une technique utilisée pour résoudre divers problèmes causés par la nécessité de résoudre des noms uniques pour les entités de programmation dans de nombreux langages de programmation modernes .

Il permet d'encoder des informations supplémentaires dans le nom d'une fonction , d'une structure , d'une classe ou d'un autre type de données , afin de transmettre davantage d'informations sémantiques du compilateur à l' éditeur de liens .

Le besoin de modification de noms se fait sentir lorsqu'un langage autorise différentes entités à porter le même identifiant , à condition qu'elles appartiennent à un espace de noms différent (généralement défini par un module, une classe ou une directive d'espace de noms explicite ) ou qu'elles possèdent des signatures de type différentes (comme dans le cas de la surcharge de fonctions ). Cette modification est nécessaire car chaque signature peut exiger une convention d'appel spécifique dans le code machine .

Le code objet produit par les compilateurs est généralement lié à d'autres fragments de code objet (produits par le même compilateur ou un autre) par un programme appelé éditeur de liens . Ce dernier a besoin de nombreuses informations sur chaque entité du programme. Par exemple, pour lier correctement une fonction, il lui faut son nom, le nombre et le type de ses arguments , etc.

Les langages de programmation simples des années 1970, comme le C , ne distinguaient les sous-programmes que par leur nom, ignorant d'autres informations telles que les types des paramètres et de retour. Les langages plus récents, comme le C++ , ont défini des exigences plus strictes pour que les programmes soient considérés comme « égaux », notamment en ce qui concerne les types des paramètres, le type de retour et la convention d'appel d'une fonction. Ces exigences permettent la surcharge de méthodes et la détection de certains bogues (comme l'utilisation de définitions différentes d'une fonction lors de la compilation de différents fichiers de code source ). Ces exigences plus strictes devaient être compatibles avec les outils et conventions de programmation existants. Par conséquent, les exigences supplémentaires étaient encodées dans le nom du symbole, car c'était la seule information dont disposait un éditeur de liens traditionnel à son sujet.

la surcharge de fonctions , comme le C et le Pascal classique , elle est parfois employée pour fournir des informations supplémentaires sur une fonction. Par exemple, les compilateurs destinés aux plateformes Microsoft Windows prennent en charge diverses conventions d'appel , qui déterminent la manière dont les paramètres sont transmis aux sous-programmes et dont les résultats sont renvoyés. Ces différentes conventions étant incompatibles, les compilateurs modifient les symboles en y ajoutant du code précisant la convention à utiliser pour appeler la routine en question.

Le système de manipulation de code pour Windows a été établi par Microsoft et est généralement adopté par d'autres compilateurs, notamment Digital Mars , Borland et GNU Compiler Collection (GCC), lors de la compilation de code pour les plateformes Windows. Ce système s'applique également à d'autres langages, tels que Pascal , D , Delphi , Fortran et C# . Cela permet aux sous-programmes écrits dans ces langages d'appeler des bibliothèques Windows existantes, ou d'être appelés par elles, en utilisant une convention d'appel différente de celle par défaut.

Lors de la compilation des exemples C suivants :

) FIN DE LA SOUS-PROGRAMME

En C, la plupart des compilateurs modifient également les fonctions et variables statiques (et en C++, les fonctions et variables déclarées statiques ou placées dans l'espace de noms anonyme) dans les unités de traduction, en utilisant les mêmes règles de modification que pour leurs versions non statiques. Si des fonctions portant le même nom (et les mêmes paramètres en C++) sont définies et utilisées dans différentes unités de traduction, elles seront également modifiées pour obtenir le même nom, ce qui peut entraîner un conflit. Cependant, elles ne seront pas équivalentes si elles sont appelées dans leurs unités de traduction respectives. Les compilateurs sont généralement libres de générer une modification arbitraire pour ces fonctions, car il est interdit d'y accéder directement depuis d'autres unités de traduction ; elles n'auront donc jamais besoin d'être liées entre différents codes objets (leur liaison est inutile). Pour éviter les conflits de liaison, les compilateurs utilisent une modification standard, mais emploient des symboles dits « locaux ». Lors de la liaison de nombreuses unités de traduction, il peut exister plusieurs définitions d'une même fonction portant le même nom, mais le code résultant n'appellera que l'une ou l'autre en fonction de l'unité de traduction d'origine. Ceci est généralement réalisé grâce au mécanisme de relocalisation .

C++

Les compilateurs C++ sont les principaux utilisateurs de la modification de noms de symboles. Les premiers compilateurs C++ étaient conçus comme des traducteurs de code source C , lequel était ensuite compilé en code objet par un compilateur C ; de ce fait, les noms de symboles devaient respecter les règles d'identification du C. Même plus tard, avec l'apparition de compilateurs produisant directement du code machine ou de l'assembleur, l' éditeur de liens du système ne prenait généralement pas en charge les symboles C++, et la modification de noms restait nécessaire.

Le langage C++ ne définit pas de système de décoration standard ; chaque compilateur utilise donc le sien. C++ possède également des fonctionnalités complexes, telles que les classes , les modèles , les espaces de noms et la surcharge d'opérateurs , qui modifient la signification de certains symboles selon le contexte ou leur utilisation. Les métadonnées relatives à ces fonctionnalités peuvent être levées par la modification (décoration) du nom d'un symbole . Comme les systèmes de modification de noms pour ces fonctionnalités ne sont pas standardisés entre les compilateurs, peu d'éditeurs de liens peuvent lier du code objet produit par différents compilateurs.

Exemple simple

Une seule unité de traduction C++ peut définir deux fonctions nommées liaison C », désactivant ainsi l’altération de nom et permettant d’appeler ces fonctions depuis le C.

Exemple complexe

Les symboles déformés dans cet exemple, dans les commentaires sous le nom d'identificateur respectif, sont ceux produits par les compilateurs GNU GCC 3.x, conformément à l' ABI IA-64 (Itanium) :

identificateur réservé en C, ce qui évite les conflits avec les identificateurs utilisateur) ; pour les noms imbriqués (y compris les espaces de noms et les classes), ceci est suivi de typedef pour Compilateur IA-64IAR EWARM C++ GCC 3.x et versions ultérieuresClang 1.x et versions supérieures GCC 2.9. xMicrosoft Visual C++ v6-v10 ( détails de manipulation ) Digital Mars C++ Borland C++ v3.1 OpenVMS C++ v6.5 (mode ARM) Compaq sur OpenVMS VAX et Alpha (mais pas IA-64) et Tru64 UNIX possède deux schémas de modification de noms. Le schéma original, antérieur à la norme, est connu sous le nom de modèle ARM et se base sur la modification de noms décrite dans le manuel de référence annoté C++ (ARM). Avec l'apparition de nouvelles fonctionnalités dans le C++ standard, notamment les modèles (templates ), le schéma ARM est devenu de plus en plus inadapté : il ne pouvait pas encoder certains types de fonctions ou produisait des noms modifiés identiques pour différentes fonctions. Il a donc été remplacé par le modèle plus récent de l' American National Standards Institute (ANSI), qui prend en charge toutes les fonctionnalités des modèles ANSI, mais n'est pas rétrocompatible.
  • Sur IA-64, une interface binaire d'application (ABI) standard existe (voir les liens externes ) qui définit (entre autres) un schéma de modification de noms standard et qui est utilisée par tous les compilateurs IA-64. GNU GCC 3.x a également adopté ce schéma de modification de noms pour une utilisation sur d'autres plateformes non Intel.
  • Le SDK Visual Studio et Windows inclut le programme Clang utilisent la modification de nom Visual C++ pour la compatibilité.
  • Gestion des symboles C lors de la liaison depuis C++

    Le rôle de l'idiome C++ courant :

    { #endif// symboles ici#ifdef __cplusplus } #endif

    Il s'agit de s'assurer que les symboles contenus dans la définition ne soient pas altérés ; autrement dit, que le compilateur produise un fichier binaire dont les noms ne soient pas modifiés, comme le ferait un compilateur C. Puisque les définitions du langage C ne sont pas altérées, le compilateur C++ doit éviter d'altérer les références à ces identificateurs.

    Par exemple, l'en-tête { #endifvoid * memset ( void * s , int c , size_t n ); char * strcat ( char * s1 , const char * s2 ); int strcmp ( const char * s1 , const char * s2 ); char * strcpy ( char * s1 , const char * s2 );#ifdef __cplusplus } #endif

    Ainsi, un code tel que :

    ) == 0 ) { strcpy ( a , argv [ 2 ]); } else { memset ( a , 0 , sizeof ( a )); }

    utilise les valeurs correctes

    ) == 0 ) { __1cGstrcpy6Fpcpkc_0_ ( a , argv [ 2 ]); } else { __1cGmemset6FpviI_0_ ( a , 0 , sizeof ( a )); }

    Comme ces symboles n'existent pas dans la bibliothèque d'exécution C ( par exemple libc), des erreurs de liaison se produiraient.

    Manipulation de noms standardisée en C++

    Il semblerait qu'une altération standardisée des noms en C++ favorise une meilleure interopérabilité entre les implémentations de compilateurs. Cependant, une telle standardisation ne suffirait pas à garantir l'interopérabilité des compilateurs C++ et pourrait même donner l'illusion d'une interopérabilité possible et sûre, alors qu'elle ne l'est pas. L'altération des noms n'est qu'un des nombreux détails de l'interface binaire d'application (ABI) que toute implémentation C++ doit définir et respecter. D'autres aspects de l'ABI, comme la gestion des exceptions , l'organisation des tables virtuelles , la structure et le remplissage des cadres de pile , sont également sources d'incompatibilité entre les différentes implémentations C++. De plus, imposer une forme particulière d'altération poserait problème aux systèmes où les limitations d'implémentation (par exemple, la longueur des symboles) imposent un schéma d'altération spécifique. Une exigence standardisée pour l'altération des noms empêcherait également une implémentation où cette altération est totalement inutile – par exemple, un éditeur de liens capable de comprendre le langage C++.

    La norme C++ ne cherche donc pas à normaliser la modification des noms. Au contraire, le manuel de référence C++ annoté (également connu sous le nom d'ARM , ISBN0-201-51459-1, la section 7.2.1c) encourage activement l’utilisation de différents schémas de manipulation pour empêcher la liaison lorsque d’autres aspects de l’ABI sont incompatibles.

    Néanmoins, comme détaillé dans la section ci-dessus, sur certaines plateformes l'ABI C++ complète a été normalisée, y compris la modification des noms.

    Effets concrets de la modification des noms en C++

    Étant donné que les symboles C++ sont couramment exportés des DLL et des fichiers objets partagés , le schéma de modification des noms n'est pas uniquement une question interne au compilateur. Différents compilateurs (ou différentes versions d'un même compilateur, dans de nombreux cas) produisent ces binaires avec des schémas de modification des noms différents, ce qui signifie que les symboles sont fréquemment non résolus si les compilateurs utilisés pour créer la bibliothèque et le programme qui l'utilise emploient des schémas différents. Par exemple, si un système disposant de plusieurs compilateurs C++ installés (par exemple, GNU GCC et le compilateur du système d'exploitation) souhaite installer les bibliothèques Boost C++ , il devra les compiler plusieurs fois (une fois pour GCC et une fois pour le compilateur du système d'exploitation).

    Pour des raisons de sécurité, il est préférable que les compilateurs produisant des codes objets incompatibles (codes basés sur des ABI différentes, concernant par exemple les classes et les exceptions) utilisent des schémas de modification de noms différents. Cela garantit que ces incompatibilités sont détectées lors de la phase d'édition de liens, et non lors de l'exécution du logiciel (ce qui pourrait entraîner des bogues difficiles à détecter et de graves problèmes de stabilité).

    Pour cette raison, la décoration des noms est un aspect important de toute ABI liée à C++ .

    Dans certains cas, notamment pour les bases de code volumineuses et complexes, il peut être difficile, voire impossible, d'associer le nom altéré affiché dans un message d'erreur de l'éditeur de liens au jeton ou au nom de variable correspondant dans le code source. Ce problème peut rendre l'identification du ou des fichiers sources concernés très difficile pour les ingénieurs de compilation ou de test, même lorsqu'un seul compilateur et un seul éditeur de liens sont utilisés. Les outils de désambiguïsation (y compris ceux intégrés aux mécanismes de rapport d'erreurs de l'éditeur de liens) peuvent parfois s'avérer utiles, mais le mécanisme d'altération lui-même peut supprimer des informations cruciales pour lever l'ambiguïté.

    Démangle via c++filt

    ; int status = -1 ; char * demangledName = abi :: __cxa_demangle ( mangledName , nullptr , nullptr , & status ); std :: println ( "Démangled: {}" , demangledName ); std :: free ( demangledName ); return 0 ; }

    Sortir:

    C# évite complètement l'utilisation de la modification de noms en s'appuyant plutôt sur les métadonnées de la représentation Common Intermediate Language . Les symboles C/C++ non modifiés (marqués avec `\ `) peuvent être utilisés avec les services d'invocation de plateforme (P/Invoke) .D utilise la transformation de noms, différente de celle du C++, et produit généralement des noms plus lisibles. Les noms sont transformés sauf lors de la liaison avec du C (via `c` ). D propose un outil `ddemangle` pour démanteler les noms de symboles. La bibliothèque offre des modules pour la transformation des symboles et pour la démanteler les symboles D. Fortran , initialement parce que le langage est insensible à la casse . D'autres exigences de modification ont été imposées ultérieurement, avec l'ajout de modules et d'autres fonctionnalités dans la norme Fortran 90. La modification de la casse, en particulier, est un problème courant qu'il faut résoudre pour appeler des bibliothèques Fortran, telles que LAPACK , depuis d'autres langages, comme le C.

    En raison de l'insensibilité à la casse, le nom d'une sous-routine ou d'une fonction d'AIX et de HP-UX convertissent tous les identificateurs en minuscules de Cray et d'Unicos les convertissent en majuscules GNU g77 convertit les identificateurs en minuscules suivies d'un trait de soulignement f2c . De nombreux autres compilateurs, notamment les compilateurs IRIX de Silicon Graphics (SGI) , GNU Fortran et le compilateur Fortran d' Intel (sauf sous Microsoft Windows), convertissent tous les identificateurs en minuscules suivies d'un trait de soulignement ( respectivement `<min> ` et `<min>`). Sous Microsoft Windows, le compilateur Fortran d'Intel utilise par défaut les majuscules sans trait de soulignement. des blocs d'interface génériques et des procédures génériques liées au type, les noms altérés n'ont pas besoin d'incorporer d'indices sur les arguments.

    L'option BIND de Fortran 2003 force le compilateur à utiliser les règles de modification utilisées pour un autre langage de programmation, tel que le C, comme indiqué ci-dessus .

    Java

    En Java , la signature d'une méthode ou d'une classe comprend son nom et les types de ses arguments et de sa valeur de retour, le cas échéant. Le format des signatures est documenté, car le langage, le compilateur et le format de fichier .class ont été conçus conjointement (en privilégiant dès le départ l'orientation objet et l'interopérabilité universelle).

    Création de noms uniques pour les classes internes et anonymes

    La portée des classes anonymes est limitée à leur classe parente. Le compilateur doit donc générer un nom public « qualifié » pour la classe interne afin d'éviter les conflits avec d'autres classes portant le même nom (internes ou non) dans le même espace de noms. De même, des noms publics « fictifs » doivent être générés pour les classes anonymes (car le concept de classes anonymes n'existe qu'au niveau du compilateur, et non à l'exécution). Ainsi, lors de la compilation du programme Java suivant :

    ; } }; } }

    généreront trois fichiers .class :

    • foo.class , contenant la classe principale (externe) foo
    • foo$bar.class , contenant la classe interne nommée foo.bar
    • foo$1.class , contenant la classe interne anonyme (locale à la méthode foo.baz )

    Tous ces noms de classes sont valides (car les symboles $ sont autorisés dans la spécification JVM) et ces noms sont « sûrs » pour le compilateur, car la définition du langage Java déconseille l'utilisation des symboles $ dans les définitions de classes Java normales.les noms pleinement qualifiés des classes ne sont uniques qu'au sein d'une instance de chargeur de classes spécifique . Les chargeurs de classes sont organisés hiérarchiquement et chaque thread de la JVM possède un chargeur de classes de contexte. Ainsi, lorsque deux instances de chargeurs de classes différentes contiennent des classes portant le même nom, le système tente d'abord de charger la classe à l'aide du chargeur de classes racine (ou système), puis descend dans la hiérarchie jusqu'au chargeur de classes de contexte.

    Interface native Java

    L'interface native Java (JNI) , qui prend en charge les méthodes natives de Java, permet aux programmes Java d'appeler des programmes écrits dans un autre langage (généralement C ou C++). Deux problèmes de résolution de noms se posent ici, et aucun n'est implémenté de manière standardisée :

    • Traduction des noms JVM vers les noms natifs : cette méthode semble plus stable, car Oracle rend son schéma public.
    • Manipulation de noms C++ normale - voir ci-dessus.

    Python

    En Python , le mangling est utilisé pour les attributs de classe que l'on ne souhaite pas voir utilisés par les sous-classes Ces attributs sont désignés comme tels en leur attribuant un nom commençant par au moins deux tirets bas et ne terminant que par un seul. Par exemple, `map` : t : Test = Test () print ([ attr for attr in dir ( t ) if "name" in attr ]) # affiche : ['_Test__mangled_name', 'normal_name']

    Pascal

    Turbo Pascal, Delphi

    Pour éviter les altérations de noms en Pascal, utilisez :

    Free Pascal prend en charge la surcharge de fonctions et d'opérateurs, et utilise donc la modification de noms pour assurer ces fonctionnalités. Par ailleurs, Free Pascal peut appeler des symboles définis dans des modules externes créés avec un autre langage et exporter ses propres symboles pour qu'ils puissent être appelés par un autre langage. Pour plus d'informations, consultez les chapitres 6.2 et 7.1 du Guide du programmeur Free Pascal .

    Objectif-C

    En Objective-C , il existe essentiellement deux types de méthodes : les méthodes de classe (« statiques ») et les méthodes d’instance . La déclaration d’une méthode en Objective-C se présente sous la forme suivante :

    + ( type de retour ) nom 0 : paramètre 0 nom 1 : paramètre 1 ... – ( type de retour ) nom 0 : paramètre 0 nom 1 : paramètre 1 ...

    Les méthodes de classe sont indiquées par +, les méthodes d'instance par -. Une déclaration de méthode de classe typique peut alors ressembler à ceci :

    le polymorphisme .

    L'environnement d'exécution Objective-C conserve des informations sur les types des arguments et de retour des méthodes. Cependant, ces informations ne font pas partie du nom de la méthode et peuvent varier d'une classe à l'autre.

    Comme Objective-C ne prend pas en charge les espaces de noms , il n'est pas nécessaire de modifier les noms de classes (qui apparaissent bien comme des symboles dans les binaires générés).

    Rouiller

    Rust . Cependant, ce comportement peut être désactivé grâce à l' Objective-C . De plus, combiné à l' un punycode modifié . La compression (référence arrière) utilise un adressage par octet. Utilisé depuis Rust 1.37.

    Des exemples sont fournis dans les Swift conserve les métadonnées des fonctions (et plus encore) dans les symboles modifiés qui y font référence. Ces métadonnées incluent le nom de la fonction, ses attributs, le nom du module, les types de paramètres, le type de retour, etc. Par exemple :

    Le nom déformé d'une méthode int"},"parts":[{"template":{"target":{"wt":"code","href":"./Template:Code"},"params":{"1":{"wt":"func calculate(x: int) -> int"}},"i":0}}] func calculate(x: int) -> intd'une

    Plus d articles de Worldlex Wiki

    Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

    Explorer l index