Articulo de referencia

Deformación de nombres

En la construcción de compiladores , la modificación de nombres (también llamada decoración de nombres ) es una técnica que se utiliza para resolver varios problemas causados ​​...

En la construcción de compiladores , la modificación de nombres (también llamada decoración de nombres ) es una técnica que se utiliza para resolver varios problemas causados ​​por la necesidad de resolver nombres únicos para entidades de programación en muchos lenguajes de programación modernos .

Proporciona medios para codificar información adicional en el nombre de una función , estructura , clase u otro tipo de datos , para pasar más información semántica del compilador al enlazador .

La necesidad de modificar nombres surge cuando un lenguaje permite que diferentes entidades se nombren con el mismo identificador siempre que ocupen un espacio de nombres diferente (normalmente definido por un módulo, una clase o una directiva de espacio de nombres explícita ) o tengan firmas de tipo diferentes (como en la sobrecarga de funciones ). Esto es necesario en estos casos porque cada firma podría requerir una convención de llamada diferente y especializada en el código máquina .

El código objeto generado por los compiladores suele enlazarse con otros fragmentos de código objeto (generados por el mismo compilador o por otro) mediante un programa llamado enlazador . El enlazador necesita mucha información sobre cada elemento del programa. Por ejemplo, para enlazar correctamente una función, necesita su nombre, el número de argumentos y sus tipos, etc.

Los lenguajes de programación sencillos de la década de 1970, como C , solo distinguían las subrutinas por su nombre, ignorando otra información, como los tipos de parámetros y de retorno. Lenguajes posteriores, como C++ , definieron requisitos más estrictos para que las rutinas se consideraran "iguales", como los tipos de parámetros, el tipo de retorno y la convención de llamada de una función. Estos requisitos permiten la sobrecarga de métodos y la detección de algunos errores (como el uso de diferentes definiciones de una función al compilar distintos archivos de código fuente ). Estos requisitos más estrictos debían ser compatibles con las herramientas y convenciones de programación existentes. Por lo tanto, los requisitos adicionales se codificaban en el nombre del símbolo, ya que esa era la única información que un enlazador tradicional tenía sobre un símbolo.

Ejemplos

do

Aunque la modificación de nombres no suele ser necesaria ni utilizada por lenguajes que no admiten sobrecarga de funciones , como C y Pascal clásico , en algunos casos la emplean para proporcionar información adicional sobre una función. Por ejemplo, los compiladores para plataformas Microsoft Windows admiten diversas convenciones de llamada , que determinan cómo se envían los parámetros a las subrutinas y cómo se devuelven los resultados. Dado que las distintas convenciones de llamada son incompatibles entre sí, los compiladores modifican los símbolos con códigos que detallan qué convención debe utilizarse para llamar a la rutina específica.

El esquema de codificación para Windows fue establecido por Microsoft y ha sido adoptado informalmente por otros compiladores, como Digital Mars , Borland y GNU Compiler Collection (GCC), al compilar código para plataformas Windows. Este esquema incluso se aplica a otros lenguajes, como Pascal , D , Delphi , Fortran y C# . Esto permite que las subrutinas escritas en dichos lenguajes llamen a bibliotecas de Windows existentes, o sean llamadas por ellas, utilizando una convención de llamada diferente a la predeterminada.

Al compilar los siguientes ejemplos en C:

int _cdecl f ( int x ) { return 0 ; }int _stdcall g ( int y ) { return 0 ; }int _fastcall h ( int z ) { return 0 ; }

Los compiladores de 32 bits emiten, respectivamente:

_F _g@4 @h@4

En los esquemas de manipulación stdcally fastcall, la función se codifica como y respectivamente, donde X es el número de bytes, en decimal, del/los argumento(s) en la lista de parámetros (incluidos los pasados ​​en registros, para fastcall). En el caso de , el nombre de la función simplemente se antepone con un guion bajo._name@X@name@Xcdecl

La convención de 64 bits en Windows (Microsoft C) no tiene guion bajo inicial. Esta diferencia puede, en algunos casos excepcionales, provocar que las variables externas no se resuelvan al portar dicho código a 64 bits. Por ejemplo, el código Fortran puede usar 'alias' para enlazar con un método C por su nombre, como se muestra a continuación:

SUBRUTINA f () !DEC$ ATRIBUTOS C, ALIAS:'_f' :: f FIN DE SUBRUTINA

Esto compilará y enlazará correctamente en 32 bits, pero generará un error externo no resuelto _fen 64 bits. Una solución alternativa es no usar 'alias' en absoluto (en el que los nombres de los métodos normalmente deben escribirse con mayúscula en C y Fortran). Otra es usar la BINDopción de Fortran 2003:

SUBRUTINA f () ENLAZAR ( C , NOMBRE = "f" ) FIN DE LA SUBRUTINA

En C, la mayoría de los compiladores también modifican las funciones y variables estáticas (y en C++ las funciones y variables declaradas como estáticas o ubicadas en el espacio de nombres anónimo) en las unidades de traducción, utilizando las mismas reglas de modificación que para sus versiones no estáticas. Si se definen y utilizan funciones con el mismo nombre (y parámetros para C++) en diferentes unidades de traducción, también se modificarán al mismo nombre, lo que podría provocar un conflicto. Sin embargo, no serán equivalentes si se llaman en sus respectivas unidades de traducción. Los compiladores suelen tener libertad para generar modificaciones arbitrarias para estas funciones, ya que es ilegal acceder a ellas directamente desde otras unidades de traducción, por lo que nunca necesitarán enlazarse entre diferentes códigos objeto (el enlace entre ellas nunca es necesario). Para evitar conflictos de enlace, los compiladores utilizarán la modificación estándar, pero usarán los llamados símbolos "locales". Al enlazar muchas de estas unidades de traducción, puede haber múltiples definiciones de una función con el mismo nombre, pero el código resultante solo llamará a una u otra dependiendo de la unidad de traducción de la que provenga. Esto generalmente se hace mediante el mecanismo de reubicación .

C++

Los compiladores de C++ son los que más utilizan la modificación de nombres de símbolos (name mangling). Los primeros compiladores de C++ se implementaron como traductores de código fuente C , que luego era compilado por un compilador C a código objeto; por ello, los nombres de los símbolos debían ajustarse a las reglas de identificación de C. Incluso más tarde, con la aparición de compiladores que generaban directamente código máquina o ensamblador, el enlazador del sistema generalmente no admitía símbolos C++, y la modificación de nombres de símbolos seguía siendo necesaria.

El lenguaje C++ no define un esquema de decoración estándar, por lo que cada compilador utiliza el suyo propio. C++ también posee características de lenguaje complejas, como clases , plantillas , espacios de nombres y sobrecarga de operadores , que modifican el significado de símbolos específicos según el contexto o el uso. Los metadatos sobre estas características pueden desambiguarse modificando (decorando) el nombre de un símbolo . Dado que los sistemas de modificación de nombres para dichas características no están estandarizados entre compiladores, pocos enlazadores pueden enlazar código objeto generado por diferentes compiladores.

Ejemplo sencillo

Una única unidad de traducción de C++ podría definir dos funciones llamadas f():

int f () { return 1 ; }int f ([[ maybe_unused ]] int x ) { return 0 ; }void g () { int i = f (); int j = f ( 0 ); }

Se trata de funciones distintas, sin relación entre sí aparte del nombre. Por lo tanto, el compilador de C++ codificará la información de tipo en el nombre del símbolo, dando como resultado algo parecido a:

int __f_v () { return 1 ; }int __f_i ([[ maybe_unused ]] int x ) { return 0 ; } void __g_v () { int i = __f_v (); int j = __f_i ( 0 ); }

Aunque su nombre es único, g()sigue siendo manglado: el mangling de nombres se aplica a todos los símbolos de C++ (excepto a los que están en un bloque). se utiliza en C++ para otorgar a un símbolo " enlace C ", deshabilitando el mangling de nombres y permitiendo la llamada a estas funciones desde C.extern"C"extern"C"

Ejemplo complejo

Los símbolos distorsionados en este ejemplo, en los comentarios debajo del nombre del identificador correspondiente, son los producidos por los compiladores GNU GCC 3.x, de acuerdo con la ABI IA-64 (Itanium):

exportar módulo wikipedia . estructura . Artículo ;importar std ;using std :: ostream ; using std :: string ; using std :: string_view ;exportar espacio de nombres wikipedia :: estructura {clase Artículo { público : artículo explícito ( nombre de la vista de cadena );~ Artículo () = predeterminado ;[[ nodiscard ]] formato de cadena (); // = _ZN9wikipedia8structure7Article6formatEvbool printTo ( ostream & os ); // = _ZN9wikipedia8structure7article6printToERSoclase Wikilink { public : explicit Wikilink ( string_view name ); // = _ZN9wikipedia8structure7Article6WikilinkC1ERKSs~ Wikilink () = predeterminado ; }; };}

Todos los símbolos modificados comienzan con _Z(tenga en cuenta que un identificador que comienza con un guion bajo seguido de una letra mayúscula es un identificador reservado en C, por lo que se evita el conflicto con los identificadores de usuario); para nombres anidados (incluidos espacios de nombres y clases), esto va seguido de N, luego una serie de pares < longitud, id > (donde la longitud es la longitud del siguiente identificador) y, finalmente E, . Por ejemplo, wikipedia::structure::Article::format()se convierte en:

_ZN9wikipedia8estructura7Artículo6formatoE

Para las funciones, a continuación se incluye la información de tipo; como format()la función no toma ningún argumento, lo cual es equivalente a format(void), esto es simplemente v; por lo tanto:

_ZN9wikipedia8estructura7Artículo6formatoEv

Para , se utiliza wikipedia::structure::Article::printTo()el tipo estándar std::ostream(que es un typedef para ), que tiene el alias especial ; por lo tanto, una referencia a este tipo es , siendo el nombre completo de la función:std::basic_ostream<char, std::char_traits<char>>SoRSo

_ZN9wikipedia8estructura7Artículo6imprimirAERSo

Tenga en cuenta que en la ABI de Itanium, el tipo de retorno se omite para las funciones que no son plantillas, pero se codifica para las funciones de plantilla [ 1 ] .

Cómo diferentes compiladores destrozan las mismas funciones

No existe un esquema estandarizado para la modificación de identificadores, incluso los más triviales, en C++, y, en consecuencia, distintos compiladores (o incluso distintas versiones del mismo compilador, o el mismo compilador en distintas plataformas) modifican los símbolos públicos de maneras radicalmente diferentes (y, por lo tanto, totalmente incompatibles). Consideremos cómo distintos compiladores de C++ modifican las mismas funciones:

Notas:

  • El compilador Compaq C++ en OpenVMS VAX y Alpha (pero no en IA-64) y Tru64 UNIX tiene dos esquemas de codificación de nombres. El esquema original, anterior al estándar, se conoce como modelo ARM y se basa en la codificación de nombres descrita en el Manual de Referencia Anotado de C++ (ARM). Con la llegada de nuevas características en C++ estándar, en particular las plantillas , el esquema ARM se volvió cada vez más inadecuado: no podía codificar ciertos tipos de funciones o producía nombres codificados idénticos para diferentes funciones. Por lo tanto, fue reemplazado por el modelo más reciente del Instituto Nacional Estadounidense de Estándares (ANSI), que admitía todas las características de las plantillas ANSI, pero no era retrocompatible.
  • En IA-64, existe una interfaz binaria de aplicación (ABI) estándar (véanse los enlaces externos ) que define (entre otras cosas) un esquema estándar de modificación de nombres, utilizado por todos los compiladores de IA-64. GNU GCC 3.x ha adoptado además el esquema de modificación de nombres definido en este estándar para su uso en otras plataformas que no sean de Intel.
  • El SDK de Visual Studio y Windows incluye un programa undnameque imprime el prototipo de función al estilo C para un nombre modificado dado.
  • En Microsoft Windows, el compilador Intel [ 3 ] y Clang [ 4 ] utilizan la modificación de nombres de Visual C++ para la compatibilidad.

Manejo de símbolos C al enlazar desde C++

La función del modismo común de C++:

#ifdef __cplusplus extern "C" { #endif// símbolos aquí#ifdef __cplusplus } #endif

El objetivo es garantizar que los símbolos internos no estén "maltratados", es decir, que el compilador genere un archivo binario con sus nombres sin modificar, como lo haría un compilador de C. Dado que las definiciones del lenguaje C no están maltratadas, el compilador de C++ debe evitar dañar las referencias a estos identificadores.

Por ejemplo, el encabezado <string.h>suele contener contenido similar a este:

#ifdef __cplusplus extern "C" { #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

Por lo tanto, código como:

if ( strcmp ( argv [ 1 ], "-x" ) == 0 ) { strcpy ( a , argv [ 2 ]); } else { memset ( a , 0 , sizeof ( a )); }

utiliza el correcto strcmpy sin modificar memset. Si no se hubiera utilizado, el compilador C++ (SunPro) produciría un código equivalente a:extern"C"

if ( __1cGstrcmp6Fpkc1_i_ ( argv [ 1 ], "-x" ) == 0 ) { __1cGstrcpy6Fpcpkc_0_ ( a , argv [ 2 ]); } else { __1cGmemset6FpviI_0_ ( a , 0 , sizeof ( a )); }

Dado que esos símbolos no existen en la biblioteca de tiempo de ejecución de C ( por ejemplo, libc), se producirían errores de enlace.

Manipulación estandarizada de nombres en C++

Parecería que la estandarización de la modificación de nombres en el lenguaje C++ conduciría a una mayor interoperabilidad entre las implementaciones de compiladores. Sin embargo, dicha estandarización por sí sola no bastaría para garantizar la interoperabilidad de los compiladores de C++ e incluso podría crear la falsa impresión de que la interoperabilidad es posible y segura cuando no lo es. La modificación de nombres es solo uno de los varios detalles de la interfaz binaria de la aplicación (ABI) que deben ser definidos y respetados por una implementación de C++. Otros aspectos de la ABI, como el manejo de excepciones , la disposición de la tabla virtual , la estructura y el relleno del marco de pila , también provocan la incompatibilidad entre diferentes implementaciones de C++. Además, exigir una forma particular de modificación de nombres causaría problemas en sistemas donde los límites de implementación (por ejemplo, la longitud de los símbolos) dictan un esquema de modificación específico. Un requisito estandarizado para la modificación de nombres también impediría una implementación donde la modificación de nombres no fuera necesaria en absoluto, por ejemplo, un enlazador que comprendiera el lenguaje C++.

Por lo tanto, el estándar C++ no intenta estandarizar la modificación de nombres. Por el contrario, el Manual de Referencia Anotado de C++ (también conocido como ARM , ISBN) 0-201-51459-1, sección 7.2.1c) fomenta activamente el uso de diferentes esquemas de manipulación para evitar la vinculación cuando otros aspectos de la ABI son incompatibles.

Sin embargo, como se detalla en la sección anterior, en algunas plataformas [ 5 ] se ha estandarizado la ABI completa de C++, incluido el manejo de nombres.

Efectos en el mundo real de la modificación de nombres en C++

Dado que los símbolos de C++ se exportan habitualmente desde archivos DLL y de objetos compartidos , el esquema de modificación de nombres no es solo una cuestión interna del compilador. Diferentes compiladores (o diferentes versiones del mismo compilador, en muchos casos) generan estos binarios con distintos esquemas de modificación de nombres, lo que significa que los símbolos suelen quedar sin resolver si los compiladores utilizados para crear la biblioteca y el programa que la utiliza emplean esquemas diferentes. Por ejemplo, si un sistema con varios compiladores de C++ instalados (por ejemplo, GNU GCC y el compilador del proveedor del sistema operativo) quisiera instalar las bibliotecas Boost C++ , tendría que compilarlas varias veces (una vez para GCC y otra para el compilador del proveedor).

Por motivos de seguridad, es recomendable que los compiladores que generan códigos objeto incompatibles (códigos basados ​​en diferentes ABI, por ejemplo, en lo que respecta a clases y excepciones) utilicen diferentes esquemas de modificación de nombres. Esto garantiza que estas incompatibilidades se detecten durante la fase de enlace, y no durante la ejecución del software (lo que podría provocar errores difíciles de detectar y graves problemas de estabilidad).

Por esta razón, la decoración de nombres es un aspecto importante de cualquier ABI relacionada con C++ .

En ocasiones, sobre todo en bases de código extensas y complejas, puede resultar difícil o poco práctico relacionar el nombre ilegible que aparece en un mensaje de error del enlazador con el token o nombre de variable correspondiente en el código fuente. Este problema puede dificultar enormemente la identificación de los archivos fuente relevantes para los ingenieros de compilación o pruebas, incluso si solo se utiliza un compilador y un enlazador. Los descifradores de nombres ilegibles (incluidos los integrados en los mecanismos de notificación de errores del enlazador) a veces son útiles, pero el propio mecanismo de descifrado puede descartar información crucial para la desambiguación.

Descomponer mediante c++filt

$ c++filt -n _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ Map<StringName, Ref<GDScript>, Comparator<StringName>, DefaultAllocator>::has(StringName const&) const

Demangle mediante ABI de GCC integrado

importar < cxxabi . h > ;importar std ;int main ( int argc , char * argv []) { const char * mangledName = "_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_" ; int status = -1 ; char * demangledName = abi :: __cxa_demangle ( mangledName , nullptr , nullptr , & status ); std :: println ( "Demangled: {}" , demangledName ); std :: free ( demangledName ); return 0 ; }

Producción:

Demangled: Map<StringName, Ref<GDScript>, Comparator<StringName>, DefaultAllocator>::has(StringName&) const

DO#

El lenguaje de programación C# evita por completo el uso de la modificación de nombres, confiando en cambio en los metadatos en la representación del Lenguaje Intermedio Común . Los símbolos C/C++ sin modificar (marcados con ) se pueden consumir con los Servicios de Invocación de Plataforma (P/Invoke) .extern"C"[DllImport]

D

El lenguaje de programación D utiliza la modificación de nombres, que se realiza de forma diferente a C++, y produce nombres generalmente más legibles para los humanos. [ 6 ] [ 7 ] Los nombres se modifican a menos que se enlace con C (usando ). Ofrece una herramienta "ddemangle" para descomponer nombres de símbolos. [ 8 ] La biblioteca ofrece módulos para modificar símbolos [ 9 ] y para descomponer símbolos D. [ 10 ]extern(C)dmd.mangleextern(D)core.demangle

Fortran

La modificación de nombres también es necesaria en los compiladores de Fortran , originalmente porque el lenguaje no distingue entre mayúsculas y minúsculas . Posteriormente, durante la evolución del lenguaje, se impusieron requisitos adicionales de modificación debido a la adición de módulos y otras características en el estándar Fortran 90. La modificación de mayúsculas y minúsculas, en particular, es un problema común que debe resolverse para llamar a bibliotecas de Fortran, como LAPACK , desde otros lenguajes , como C.

Debido a la insensibilidad a mayúsculas y minúsculas, el nombre de una subrutina o función FOOdebe ser convertido a un formato y mayúsculas estandarizados por el compilador para que se enlace de la misma manera independientemente de las mayúsculas y minúsculas. Diferentes compiladores han implementado esto de diversas maneras, y no se ha producido una estandarización. Los compiladores Fortran de AIX y HP-UX convierten todos los identificadores a minúsculas foo, mientras que los compiladores Fortran de Cray y Unicos los convierten a mayúsculas FOO. El compilador GNU g77 convierte los identificadores a minúsculas más un guion bajo foo_, excepto que a los identificadores que ya contienen un guion bajo FOO_BARse les añaden dos guiones bajos foo_bar__, siguiendo una convención establecida por f2c . Muchos otros compiladores, incluidos los compiladores IRIX de Silicon Graphics (SGI) , GNU Fortran y el compilador Fortran de Intel (excepto en Microsoft Windows), convierten todos los identificadores a minúsculas más un guion bajo ( y , respectivamente). En Microsoft Windows, el compilador Fortran de Intel utiliza mayúsculas por defecto sin guion bajo. [ 11 ]foo_foo_bar_

Los identificadores en los módulos de Fortran 90 deben ser modificados aún más, ya que el mismo nombre de procedimiento puede aparecer en diferentes módulos. Dado que el estándar Fortran 2003 exige que los nombres de los procedimientos de los módulos no entren en conflicto con otros símbolos externos, [ 12 ] los compiladores tienden a usar el nombre del módulo y el nombre del procedimiento, con un marcador distintivo entre ellos. Por ejemplo:

El módulo m contiene la función entera five () five = 5 fin de la función five fin del módulo m

En este módulo, el nombre de la función se modificará de la siguiente manera: __m_MOD_five(por ejemplo, GNU Fortran), m_mp_five_(por ejemplo, ifort de Intel), m.five_(por ejemplo, sun95 de Oracle), etc. Dado que Fortran no permite sobrecargar el nombre de un procedimiento, sino que utiliza bloques de interfaz genéricos y procedimientos genéricos ligados a tipos, los nombres modificados no necesitan incorporar pistas sobre los argumentos.

La opción BIND de Fortran 2003 hace que el compilador utilice las reglas de modificación de caracteres utilizadas para otro lenguaje de programación, como C, como se muestra arriba .

Java

En Java , la firma de un método o una clase contiene su nombre y los tipos de sus argumentos y valor de retorno, cuando corresponda. El formato de las firmas está documentado, ya que el lenguaje, el compilador y el formato de archivo .class se diseñaron conjuntamente (teniendo en cuenta desde el principio la orientación a objetos y la interoperabilidad universal).

Creación de nombres únicos para clases internas y anónimas

El alcance de las clases anónimas se limita a su clase padre, por lo que el compilador debe generar un nombre público "calificado" para la clase interna , para evitar conflictos cuando existan otras clases con el mismo nombre (internas o no) en el mismo espacio de nombres. De manera similar, se deben generar nombres públicos "falsos" para las clases anónimas (ya que el concepto de clases anónimas solo existe en el compilador, no en el entorno de ejecución). Por lo tanto, compilando el siguiente programa Java:

clase pública Foo { clase Bar { público int x ; }public void baz () { Object f = new Object () { public String toString () { return "hola" ; } }; } }

generará tres archivos .class :

  • foo.class , que contiene la clase principal (externa) foo
  • foo$bar.class , que contiene la clase interna denominada foo.bar
  • foo$1.class , que contiene la clase interna anónima (local al método foo.baz )

Todos estos nombres de clase son válidos (ya que los símbolos $ están permitidos en la especificación de la JVM) y son "seguros" para que el compilador los genere, ya que la definición del lenguaje Java recomienda no usar símbolos $ en las definiciones de clases Java normales.

La resolución de nombres en Java se complica aún más en tiempo de ejecución, ya que los nombres completos de las clases son únicos solo dentro de una instancia específica del cargador de clases . Los cargadores de clases están ordenados jerárquicamente y cada hilo en la JVM tiene un cargador de clases de contexto. Por lo tanto, en los casos en que dos instancias diferentes de cargadores de clases contienen clases con el mismo nombre, el sistema primero intenta cargar la clase usando el cargador de clases raíz (o del sistema) y luego desciende en la jerarquía hasta el cargador de clases de contexto.

Interfaz nativa de Java

La interfaz nativa de Java (Java Native Interface) , el soporte para métodos nativos de Java, permite que los programas escritos en Java llamen a programas escritos en otro lenguaje (normalmente C o C++). Aquí surgen dos problemas de resolución de nombres, ninguno de los cuales está implementado de forma estandarizada :

  • Traducción de nombres de JVM a nativos: esto parece ser más estable, ya que Oracle hace público su esquema. [ 13 ]
  • Manipulación normal de nombres en C++ - ver arriba.

Pitón

En Python , el mangling se utiliza para los atributos de clase que no se desea que las subclases utilicen [ 14 ] , los cuales se designan como tales dándoles un nombre con dos o más guiones bajos iniciales y no más de un guion bajo final. Por ejemplo, __thingse aplicará mangling, al igual que ___thingy __thing_, pero __thing__y __thing___no. El entorno de ejecución de Python no restringe el acceso a dichos atributos; el mangling solo evita colisiones de nombres si una clase derivada define un atributo con el mismo nombre.

Al encontrar atributos con nombres distorsionados, Python transforma estos nombres anteponiendo un guion bajo y el nombre de la clase que los contiene, por ejemplo:

clase Test : def __mangled_name ( self ) -> None : passdef normal_name ( self ) -> None : passif __name == "__main__" : t : Test = Test () print ([ attr for attr in dir ( t ) if "name" in attr ]) # imprime: ['_Test__mangled_name', 'normal_name']

Pascal

Turbo Pascal, Delphi

Para evitar la modificación de nombres en Pascal, utilice:

exporta myFunc nombre 'myFunc' , myProc nombre 'myProc' ;

Pascal libre

Free Pascal admite la sobrecarga de funciones y operadores, por lo que también utiliza la modificación de nombres para dar soporte a estas características. Por otro lado, Free Pascal puede llamar a símbolos definidos en módulos externos creados con otro lenguaje y exportar sus propios símbolos para que sean llamados por otro lenguaje. Para más información, consulte los capítulos 6.2 y 7.1 de la Guía del programador de Free Pascal .

Objetivo-C

En Objective-C existen esencialmente dos tipos de métodos : el método de clase ("estático") y el método de instancia . La declaración de un método en Objective-C tiene la siguiente forma:

+ ( tipo de retorno ) nombre 0 : parámetro 0 nombre 1 : parámetro 1 ... – ( tipo de retorno ) nombre 0 : parámetro 0 nombre 1 : parámetro 1 ...

Los métodos de clase se representan con +, mientras que los métodos de instancia utilizan -. Una declaración típica de un método de clase podría tener el siguiente aspecto:

+ ( id ) initWithX: ( int ) number andY: ( int ) number ; + ( id ) new ;

Con métodos de instancia que se ven así:

- ( id ) valor ; - ( id ) establecerValor: ( id ) nuevo_valor ;

Cada una de estas declaraciones de método tiene una representación interna específica. Al compilarse, cada método se nombra según el siguiente esquema para métodos de clase:

_c_ Clase _ nombre 0 _ nombre 1 _ ...

y esto por ejemplo métodos:

_i_ Clase _ nombre 0 _ nombre 1 _ ...

Los dos puntos en la sintaxis de Objective-C se traducen a guiones bajos. Por lo tanto, el método de clase de Objective-C , si pertenece a la clase, se traduciría como , y el método de instancia (que pertenece a la misma clase) se traduciría a .+(id)initWithX:(int)numberandY:(int)number;Point_c_Point_initWithX_andY_-(id)value;_i_Point_value

Cada uno de los métodos de una clase se etiqueta de esta manera. Sin embargo, buscar un método al que una clase pueda responder sería tedioso si todos los métodos se representaran de esta forma. A cada método se le asigna un símbolo único (como un número entero). Dicho símbolo se conoce como selector . En Objective-C, se pueden gestionar los selectores directamente; tienen un tipo específico en Objective-C SEL.

Durante la compilación, se crea una tabla que asigna la representación textual, como _i_Point_value, a selectores (a los que se les da un tipo SEL). Gestionar selectores es más eficiente que manipular la representación textual de un método. Tenga en cuenta que un selector solo coincide con el nombre de un método, no con la clase a la que pertenece: diferentes clases pueden tener diferentes implementaciones de un método con el mismo nombre. Debido a esto, a las implementaciones de un método también se les da un identificador específico, estos se conocen como punteros de implementación, y también se les da un tipo, IMP.

El compilador codifica los envíos de mensajes como llamadas a la función, o a una de sus variantes, donde es el receptor del mensaje y determina el método a llamar. Cada clase tiene su propia tabla que asigna selectores a sus implementaciones; el puntero de implementación especifica dónde reside en la memoria la implementación del método. Hay tablas separadas para métodos de clase e instancia. Aparte de estar almacenadas en las tablas de búsqueda, las funciones son esencialmente anónimas.idobjc_msgSend(idreceiver,SELselector,...)receiverSELSELIMP

El SELvalor de un selector no varía entre clases. Esto permite el polimorfismo .

El entorno de ejecución de Objective-C mantiene información sobre los tipos de argumentos y de retorno de los métodos. Sin embargo, esta información no forma parte del nombre del método y puede variar de una clase a otra.

Dado que Objective-C no admite espacios de nombres , no es necesario modificar los nombres de las clases (que sí aparecen como símbolos en los binarios generados).

Óxido

En Rust , los nombres de las funciones se modifican de forma predeterminada . Sin embargo, esto se puede deshabilitar mediante el #[unsafe(no_mangle)]atributo `function`. Este atributo permite exportar funciones a C, C++ u Objective-C . [ 15 ] Además, junto con el #[start]atributo `function` o el #![no_main]atributo `crate`, permite al usuario definir un punto de entrada al estilo C para el programa. [ 16 ]

Rust ha utilizado muchas versiones de esquemas de modificación de símbolos que se pueden seleccionar en tiempo de compilación mediante una -Z symbol-mangling-versionopción. Se definen los siguientes modificadores:

  • legacyUn estilo de manipulación de caracteres en C++ basado en la ABI de C++ de Itanium IA-64. Los símbolos comienzan con _ZN, y se utilizan hashes de nombres de archivo para la desambiguación. Se utiliza desde Rust 1.9. [ 17 ]
  • v0Una versión mejorada del esquema anterior, con cambios para Rust. Los símbolos comienzan con _R. El polimorfismo se puede codificar. Las funciones no tienen tipos de retorno codificados (Rust no tiene sobrecarga). Los nombres Unicode usan punycode modificado . La compresión (referencia inversa) usa direccionamiento basado en bytes. Usado desde Rust 1.37. [ 18 ]

En las symbol-namespruebas de Rust se proporcionan ejemplos. [ 19 ]

Rápido

Swift almacena metadatos sobre las funciones (y más) en los símbolos modificados que las identifican. Estos metadatos incluyen el nombre de la función, sus atributos, el nombre del módulo, los tipos de parámetros, el tipo de retorno y más. Por ejemplo:

El nombre modificado para un método func calculate(x: int) -> intde una MyClassclase en el módulo testes _TFC4test7MyClass9calculatefS0_FT1xSi_Si, para Swift 2014. Los componentes y sus significados son los siguientes: [ 20 ]

  • _T: El prefijo para todos los símbolos de Swift. Todo comenzará con esto.
  • F: Función no currificada.
  • C: Función de una clase, es decir, un método
  • 4test: Nombre del módulo, precedido por su longitud.
  • 7MyClass: Nombre de la clase a la que pertenece la función, precedido por su longitud.
  • 9calculate: Nombre de la función, precedido por su longitud.
  • f: El atributo de función. En este caso, 'f', que significa una función normal.
  • S0: Designa el tipo del primer parámetro (es decir, la instancia de la clase) como el primero en la pila de tipos (aquí MyClassno está anidado y, por lo tanto, tiene el índice 0).
  • _FT: Aquí comienza la lista de tipos para la tupla de parámetros de la función.
  • 1x: Nombre externo del primer parámetro de la función.
  • Si: Indica el tipo Swift.Int integrado para el primer parámetro.
  • _Si: El tipo de retorno: nuevamente Swift.Int.

La modificación de tipos para versiones posteriores a Swift 4.0 está documentada oficialmente. Conserva cierta similitud con Itanium. [ 21 ]

Véase también

Referencias

  1. Especificación ABI de Itanium C++ 5.1.5.3 Tipos de función
  2. Clang - Características y objetivos: Compatibilidad con GCC , 15 de abril de 2013
  3. "Diferencias de OBJ entre el compilador Intel y el compilador VC" . software.intel.com . Archivado del original el 20 de mayo de 2022.
  4. "Compatibilidad con MSVC" . Consultado el 13 de mayo de 2016 .
  5. ^ "Itanium C++ ABI, sección 5.1 Nombres externos (también conocido como Mangling)" . Consultado el 16 de mayo de 2016 .
  6. ^ Fundación D Language (10 de octubre de 2025). "La novedosa manipulación de nombres de D" . dlang.org . Fundación de la Lengua D.
  7. Rainer Schueltze (20 de diciembre de 2017). "Interfaz binaria de aplicaciones - Lenguaje de programación D" . dlang.org . Fundación del lenguaje D.
  8. Dlang. "tools/ddmangle.d en master" . github.com . Dlang . Consultado el 6 de enero de 2026 .
  9. Fundación del Lenguaje D (10 de octubre de 2025). "dmd.mangle - Lenguaje de programación D" . dlang.org . Fundación del Lenguaje D.
  10. Fundación del Lenguaje D (10 de octubre de 2025). "core.demangle - Lenguaje de programación D" . dlang.org . Fundación del Lenguaje D.
  11. "Resumen de problemas de lenguajes mixtos" . Guía del usuario y de referencia para el compilador Intel Fortran 15.0 . Intel Corporation . Consultado el 17 de noviembre de 2014 .
  12. "Biblioteca de documentación" .
  13. "Descripción general del diseño" . docs.oracle.com .
  14. "PEP 8 -- Guía de estilo para código Python" .
  15. "Interfaz de funciones externas # Llamada a código Rust desde C" . Manual de Rust . rust-lang.org . Consultado el 13 de mayo de 2016 .
  16. "Sin biblioteca estándar" . Manual de Rust . rust-lang.org . Consultado el 13 de mayo de 2016 .
  17. "rust/src/librustc_codegen_utils/symbol_names/legacy.r.rs en 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · rust-lang/rust · GitHub" . GitHub . 3 de noviembre de 2021.
  18. "Manipulación de símbolos de Rust" . El libro de RFC de Rust .
  19. "Rust 1.42.0: src/test/ui/symbol-names" . Github . Consultado el 20 de marzo de 2020 .
  20. "mikeash.com: Preguntas y respuestas del viernes 15/08/2014: Distorsión de nombres de Swift" . mikeash.com .
  21. "apple/swift: mangling.rst" . GitHub . 3 de noviembre de 2021.
  • Interfaz binaria de aplicación (ABI) de Linux Itanium para C++ , incluyendo el esquema de modificación de nombres.
  • Especificación estándar ABI de C/C++ para Macintosh
  • c++filt : filtro para descifrar símbolos C++ codificados para compiladores GNU/Intel.
  • undname : herramienta de MSVC para descifrar nombres.
  • demangler.com – Una herramienta en línea para descomponer símbolos de C++ de GCC y MSVC.
  • El sistema de tiempo de ejecución de Objective-C – Del libro "El lenguaje de programación Objective-C 1.0" de Apple.
  • El libro "Calling Conventions for different C++ Compilers" de Agner Fog contiene una descripción detallada de los esquemas de modificación de nombres para varios compiladores C++ x86 y x64 (págs.  24-42 en la versión del 8 de junio de 2011).
  • Manipulación/demangling de nombres en C++ Explicación bastante detallada del esquema de manipulación de nombres del compilador Visual C++
  • PHP UnDecorateSymbolName es un script PHP que decodifica los nombres de las funciones de Microsoft Visual C++.
  • Combinando código C y C++
  • Levine, John R. (2000) [octubre de 1999]. «Capítulo 5: Gestión de símbolos». Enlazadores y cargadores . Serie Morgan Kaufmann de ingeniería de software y programación (1.ª  ed.). San Francisco, EE. UU.: Morgan Kaufmann . ISBN 1-55860-496-0. OCLC 42413382 . Consultado el 12 de enero de 2020 . {{cite book}}: CS1 maint: servicio de archivado obsoleto ( enlace ) Código:Erratas:
  • Fivos Kefallonitis desmitifica la distorsión de nombres.