Jump to content

Искажение имени

В компилятора конструкции искажение имен (также называемое украшением имени ) — это метод, используемый для решения различных проблем, вызванных необходимостью разрешения уникальных имен для объектов программирования во многих современных языках программирования .

Он предоставляет средства для кодирования добавленной информации в имя функции , структуры , класса или другого типа данных , чтобы передать больше семантической информации от компилятора к компоновщику .

Необходимость в искажении имен возникает, когда язык позволяет называть разные объекты одним и тем же идентификатором , пока они занимают другое пространство имен (обычно определяемое модулем, классом или явной директивой пространства имен ) или имеют разные сигнатуры типов (например, в перегрузка функции ). Это необходимо в таких случаях, поскольку для каждой подписи может потребоваться различное специализированное соглашение о вызовах в машинном коде .

Любой объектный код , созданный компиляторами, обычно связывается с другими частями объектного кода (созданными тем же или другим компилятором) с помощью программы, называемой компоновщиком . Компоновщику требуется большой объем информации о каждом программном объекте. Например, чтобы правильно связать функцию, ей нужно ее имя, количество аргументов и их типы и так далее.

Простые языки программирования 1970-х годов, такие как C , различали подпрограммы только по их имени, игнорируя другую информацию, включая типы параметров и возвращаемых значений. Более поздние языки, такие как C++ , определили более строгие требования к подпрограммам, которые должны считаться «равными», такие как типы параметров, тип возвращаемого значения и соглашение о вызове функции. Эти требования позволяют перегружать методы и обнаруживать некоторые ошибки (например, использование разных определений функции при компиляции разных файлов исходного кода ). Эти более строгие требования необходимы для работы с существующими инструментами и соглашениями программирования . Таким образом, дополнительные требования были закодированы в имени символа, поскольку это была единственная информация, которую традиционный компоновщик имел о символе.

Другое использование искажения имен — обнаружение добавленных изменений, не связанных с сигнатурой, таких как чистота функции или возможность потенциального возникновения исключения или запуска сборки мусора . Примером языка, делающего это, является D . [1] [2] Это скорее упрощенная проверка ошибок. Например, функции int f(); и int g(int) pure; могли быть скомпилированы в один объектный файл, но тогда их подписи менялись на float f(); int g(int); и использовался для компиляции другого источника, вызывающего его. Во время компоновки компоновщик обнаружит отсутствие функции. f(int) и вернуть ошибку. Аналогично, компоновщик не сможет определить, что тип возвращаемого значения f отличается и возвращает ошибку. В противном случае будут использоваться несовместимые соглашения о вызовах, что, скорее всего, приведет к неправильному результату или приведет к сбою программы. Манглирование обычно не фиксирует каждую деталь процесса вызова. Например, он не полностью предотвращает такие ошибки, как изменение элементов данных структуры или класса. Например, struct S {}; void f(S) {} можно скомпилировать в один объектный файл, тогда определение для S изменился, чтобы быть struct S { int x; }; и используется при составлении вызова f(S()). В таких случаях компилятор обычно использует другое соглашение о вызовах, но в обоих случаях f будет преобразовано в одно и то же имя, поэтому компоновщик не обнаружит эту проблему, и результатом обычно будет сбой или повреждение данных или памяти во время выполнения.

Примеры [ править ]

С [ править ]

Хотя искажение имен обычно не требуется и не используется в языках, которые не поддерживают перегрузку функций , таких как C и классический Pascal , они используют его в некоторых случаях для предоставления дополнительной информации о функции. Например, компиляторы, предназначенные для платформ Microsoft Windows , поддерживают различные соглашения о вызовах , которые определяют способ отправки параметров в подпрограммы и возврата результатов. Поскольку различные соглашения о вызовах несовместимы друг с другом, компиляторы искажают символы кодами, подробно описывающими, какое соглашение следует использовать для вызова конкретной процедуры.

Схема искажения для Windows была создана Microsoft, и ей неофициально следовали другие компиляторы, включая Digital Mars , Borland и GNU Compiler Collection (GCC), при компиляции кода для платформ Windows. Схема применима даже к другим языкам, таким как Pascal , D , Delphi , Fortran и C# . Это позволяет подпрограммам, написанным на этих языках, вызывать существующие библиотеки Windows, используя соглашение о вызовах, отличное от используемого по умолчанию.

При компиляции следующих примеров C:

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

32-битные компиляторы выдают соответственно:

_f
_g@4
@h@4

В stdcall и fastcall схем искажения, функция кодируется как _name@X и @name@X соответственно, где X — количество байтов в десятичном формате аргумента(ов) в списке параметров (включая переданные в регистрах для быстрого вызова). В случае cdecl, имя функции просто начинается с подчеркивания.

В 64-битном соглашении Windows (Microsoft C) нет ведущего подчеркивания. Эта разница может в некоторых редких случаях привести к неразрешенным внешним проблемам при переносе такого кода на 64-битную версию. Например, код Фортрана может использовать «псевдоним» для связи с методом C по имени следующим образом:

SUBROUTINE f()
!DEC$ ATTRIBUTES C, ALIAS:'_f' :: f
END SUBROUTINE

Это будет нормально компилироваться и компоноваться под 32-битной версией, но генерировать неразрешенный внешний файл. _f под 64 бита. Одним из способов решения этой проблемы является вообще не использовать «псевдоним» (в котором имена методов обычно должны быть написаны с заглавной буквы в C и Fortran). Другой способ — использовать опцию BIND:

SUBROUTINE f() BIND(C,NAME="f")
END SUBROUTINE

В C большинство компиляторов также преобразуют статические функции и переменные (а в C++ функции и переменные, объявленные как статические или помещенные в анонимное пространство имен) в единицах перевода, используя те же правила преобразования, что и для их нестатических версий. Если функции с одинаковым именем (и параметрами для C++) также определены и используются в разных единицах перевода, это также приведет к тому же имени, что потенциально может привести к конфликту. Однако они не будут эквивалентны, если вызываются в соответствующих единицах перевода. Компиляторы обычно могут произвольно искажать эти функции, поскольку прямой доступ к ним из других единиц трансляции незаконен, поэтому им никогда не потребуется связывание между различными объектными кодами (их связывание никогда не требуется). Чтобы предотвратить конфликты ссылок, компиляторы будут использовать стандартное искажение, но будут использовать так называемые «локальные» символы. При связывании многих таких единиц перевода может быть несколько определений функции с одним и тем же именем, но результирующий код будет вызывать только одно или другое в зависимости от того, из какой единицы перевода он получен. Обычно это делается с помощью перемещения механизм .

С++ [ править ]

Компиляторы C++ являются наиболее распространенными пользователями искажения имен. Первые компиляторы C++ были реализованы как трансляторы исходного кода C , который затем компилировался компилятором C в объектный код; из-за этого имена символов должны были соответствовать правилам идентификаторов C. Даже позже, с появлением компиляторов, которые создавали машинный код или ассемблер напрямую, системный компоновщик обычно не поддерживал символы C++, и искажения по-прежнему требовались.

Язык C++ не определяет стандартную схему оформления, поэтому каждый компилятор использует свою собственную. C++ также имеет сложные функции языка, такие как классы , шаблоны , пространства имен и перегрузка операторов , которые изменяют значение определенных символов в зависимости от контекста или использования. Метаданные об этих функциях можно устранить, исказив (украсив) имя символа . Поскольку системы изменения имен для таких функций не стандартизированы для разных компиляторов, немногие компоновщики могут связывать объектный код, созданный разными компиляторами.

Простой пример [ править ]

Одна единица трансляции C++ может определять две функции с именами f():

int  f () { return 1; }
int  f (int)  { return 0; }
void g () { int i = f(), j = f(0); }

Это отдельные функции, не имеющие никакой связи друг с другом, кроме названия. Поэтому компилятор C++ закодирует информацию о типе в имени символа, в результате чего получится что-то вроде:

int  __f_v () { return 1; }
int  __f_i (int)  { return 0; } 
void __g_v () { int i = __f_v(), j = __f_i(0); }

Несмотря на то, что его имя уникально, g() все еще искажено: искажение имен применяется ко всем символам C++ (за исключением символов в extern "C"{} блокировать).

Сложный пример [ править ]

Искаженные символы в этом примере в комментариях под соответствующим именем идентификатора созданы компиляторами GNU GCC 3.x в соответствии с ABI IA-64 (Itanium):

namespace wikipedia 
{
   class article 
   {
   public:
      std::string format ();  // = _ZN9wikipedia7article6formatEv

      bool print_to (std::ostream&);  // = _ZN9wikipedia7article8print_toERSo

      class wikilink 
      {
      public:
         wikilink (std::string const& name);  // = _ZN9wikipedia7article8wikilinkC1ERKSs
      };
   };
}

Все искаженные символы начинаются с _Z (обратите внимание, что идентификатор, начинающийся с подчеркивания, за которым следует заглавная буква, является зарезервированным идентификатором в C, поэтому можно избежать конфликта с идентификаторами пользователя); для вложенных имен (включая пространства имен и классы) за ним следует N, затем серия пар <length, id> (длина равна длине следующего идентификатора) и, наконец, E. Например, wikipedia::article::format становится:

_ZN9wikipedia7article6formatE

Для функций за этим следует информация о типе; как format() это void функция, это просто v; следовательно:

_ZN9wikipedia7article6formatEv

Для print_to, стандартный тип std::ostream (что представляет собой определение типа для std::basic_ostream<char, std::char_traits<char> >) используется специальный псевдоним So; поэтому ссылка на этот тип RSo, полное имя функции:

_ZN9wikipedia7article8print_toERSo

Как разные компиляторы искажают одни и те же функции [ править ]

Не существует стандартизированной схемы, по которой искажаются даже тривиальные идентификаторы C++, и, следовательно, разные компиляторы (или даже разные версии одного и того же компилятора или один и тот же компилятор на разных платформах) искажают общедоступные символы в радикально разные (и, следовательно, совершенно несовместимые) символы. пути. Посмотрите, как разные компиляторы C++ искажают одни и те же функции:

Компилятор void h(int) void h(int, char) void h(void)
Intel C++ 8.0 для Linux _Z1hi _Z1hic _Z1hv
HP aC++ A.05.55 IA-64
И EWARM C++
GCC 3.x и выше
Clang 1.x и выше [3]
ОКС 2.9. х h__Fi h__Fic h__Fv
HP aC++ A.03.45 PA-RISC
Microsoft Visual C++ v6-v10 ( искажение деталей ) ?h@@YAXH@Z ?h@@YAXHD@Z ?h@@YAXXZ
Цифровой Марс C++
Борланд С++ v3.1 @h$qi @h$qizc @h$qv
OpenVMS C++ v6.5 (режим ARM) H__XI H__XIC H__XV
OpenVMS C++ v6.5 (режим ANSI) CXX$__7H__FIC26CDH77 CXX$__7H__FV2CB06E8
OpenVMS С++ X7.1 IA-64 CXX$_Z1HI2DSQ26A CXX$_Z1HIC2NP3LI4 CXX$_Z1HV0BCA19V
СанПро CC __1cBh6Fi_v_ __1cBh6Fic_v_ __1cBh6F_v_
Tru64 C++ v6.5 (режим ARM) h__Xi h__Xic h__Xv
Tru64 C++ v6.5 (режим ANSI) __7h__Fi __7h__Fic __7h__Fv
Ватком С++ 10.6 W?h$n(i)v W?h$n(ia)v W?h$n()v

Примечания:

  • Компилятор Compaq (но не IA - C++ для OpenVMS VAX и Alpha 64) и Tru64 UNIX имеет две схемы искажения имен. Исходная предстандартная схема известна как модель ARM и основана на искажении имен, описанном в аннотированном справочном руководстве C++ (ARM). С появлением новых функций в стандарте C++, в частности шаблонов , схема ARM становилась все более и более непригодной — она не могла кодировать определенные типы функций или создавать одинаково искаженные имена для разных функций. Поэтому она была заменена более новой моделью Американского национального института стандартов (ANSI), которая поддерживала все функции шаблона ANSI, но не имела обратной совместимости.
  • стандартный двоичный интерфейс приложений В IA-64 существует (ABI) (см. внешние ссылки ), который определяет (помимо прочего) стандартную схему изменения имен и используется всеми компиляторами IA-64. GNU GCC 3. x дополнительно принял схему изменения имен, определенную в этом стандарте, для использования на других платформах, отличных от Intel.
  • Visual Studio и Windows SDK включают программу. undname который печатает прототип функции в стиле C для заданного искаженного имени.
  • В Microsoft Windows компилятор Intel [4] и Кланг [5] использует преобразование имен Visual C++ для совместимости.

Обработка символов C при связывании с C++ [ править ]

Работа общей идиомы C++:

#ifdef __cplusplus 
extern "C" {
#endif
    /* ... */
#ifdef __cplusplus
}
#endif

заключается в том, чтобы гарантировать, что символы внутри «неискажены» - что компилятор создает двоичный файл с недекорированными именами, как это сделал бы компилятор C. Поскольку определения языка C не искажены, компилятору C++ необходимо избегать искажения ссылок на эти идентификаторы.

Например, стандартная библиотека строк, <string.h>, обычно содержит что-то вроде:

#ifdef __cplusplus
extern "C" {
#endif

void *memset (void *, int, size_t);
char *strcat (char *, const char *);
int   strcmp (const char *, const char *);
char *strcpy (char *, const char *);

#ifdef __cplusplus
}
#endif

Таким образом, такой код, как:

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

использует правильный, неиспорченный strcmp и memset. Если extern "C" не использовался, компилятор (SunPro) C++ выдал бы код, эквивалентный:

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

Поскольку эти символы не существуют в библиотеке времени выполнения C ( например, libc), возникнут ошибки компоновки.


Стандартизированное искажение имен в C++ [ править ]

Казалось бы, стандартизированное изменение имен в языке C++ приведет к большей совместимости между реализациями компилятора. Однако такой стандартизации самой по себе недостаточно, чтобы гарантировать совместимость компиляторов C++, и она может даже создать ложное впечатление, что совместимость возможна и безопасна, хотя на самом деле это не так. Искажение имен — это лишь одна из нескольких деталей двоичного интерфейса приложения (ABI), которые должны быть решены и соблюдены реализацией C++. Другие аспекты ABI, такие как обработка исключений , макет виртуальной таблицы , структура и заполнение кадров стека, также приводят к несовместимости различных реализаций C++. Кроме того, требование определенной формы искажения может вызвать проблемы для систем, в которых ограничения реализации (например, длина символов) диктуют конкретную схему искажения. Стандартизированное требование по изменению имен также предотвратит реализацию, в которой искажение вообще не требуется — например, компоновщик, который понимает язык C++.

Поэтому стандарт C++ не пытается стандартизировать искажение имен. Напротив, Справочное руководство по C++ с аннотациями (также известное как ARM , ISBN   0-201-51459-1 , раздел 7.2.1c) активно поощряет использование различных схем искажения для предотвращения связывания, когда другие аспекты ABI несовместимы.

Тем не менее, как подробно описано в разделе выше, на некоторых платформах [6] Полный C++ ABI был стандартизирован, включая искажение имен.

Реальные последствия искажения имен C++ [ править ]

Поскольку символы C++ обычно экспортируются из DLL и общих объектных файлов, схема искажения имен — это не просто внутренняя проблема компилятора. Разные компиляторы (или во многих случаях разные версии одного и того же компилятора) создают такие двоичные файлы с разными схемами оформления имен, а это означает, что символы часто не разрешаются, если компиляторы, используемые для создания библиотеки, и использующая ее программа использовали разные схемы. Например, если система с несколькими установленными компиляторами C++ (например, GNU GCC и компилятор поставщика ОС) желает установить библиотеки Boost C++ , ее придется скомпилировать несколько раз (один раз для GCC и один раз для компилятора поставщика).

В целях безопасности полезно, чтобы компиляторы, создающие несовместимые объектные коды (коды, основанные на разных ABI, например, в отношении классов и исключений), использовали разные схемы искажения имен. Это гарантирует, что эти несовместимости будут обнаружены на этапе компоновки, а не при запуске программного обеспечения (что может привести к неявным ошибкам и серьезным проблемам со стабильностью).

По этой причине украшение имени является важным аспектом любого ABI , связанного с C++ .

Бывают случаи, особенно в больших и сложных базах кода, когда может быть сложно или непрактично сопоставить искаженное имя, выдаваемое в сообщении об ошибке компоновщика, обратно с конкретным соответствующим именем токена/переменной в источнике. Эта проблема может затруднить идентификацию соответствующих исходных файлов для инженеров по сборке или тестированию, даже если используются только один компилятор и компоновщик. Деманглеры (в том числе входящие в механизмы сообщения об ошибках компоновщика) иногда помогают, но сам механизм искажения может отбросить важную информацию, устраняющую неоднозначность.

Разборка с помощью c++filt [ править ]

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

Деманглирование через встроенный GCC ABI [ править ]

#include <stdio.h>
#include <stdlib.h>
#include <cxxabi.h>

int main() {
	const char *mangled_name = "_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_";
	int status = -1;
	char *demangled_name = abi::__cxa_demangle(mangled_name, NULL, NULL, &status);
	printf("Demangled: %s\n", demangled_name);
	free(demangled_name);
	return 0;
}

Выход:

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

Ява [ править ]

В Java подпись . метода или класса содержит его имя, типы аргументов метода и возвращаемое значение, если это применимо Формат подписей документирован, поскольку язык, компилятор и формат файла .class были разработаны вместе (и с самого начала учитывались объектная ориентация и универсальная совместимость).

Создание уникальных имен для внутренних и анонимных классов [ править ]

Область действия анонимных классов ограничена их родительским классом, поэтому компилятор должен создать «полное» общедоступное имя для внутреннего класса , чтобы избежать конфликта, когда другие классы с тем же именем (внутренним или нет) существуют в том же пространстве имен. Аналогично, анонимные классы должны иметь «поддельные» общедоступные имена, сгенерированные для них (поскольку концепция анонимных классов существует только в компиляторе, а не во время выполнения). Итак, компилируем следующую Java-программу:

public class foo {
    class bar {
        public int x;
    }

    public void zark () {
        Object f = new Object () {
            public String toString() {
                return "hello";
            }
        };
    }
}

создаст три .class файла :

  • foo.class , содержащий основной (внешний) класс foo
  • foo$bar.class , содержащий именованный внутренний класс foo.bar
  • foo$1.class , содержащий анонимный внутренний класс (локальный для метода foo.zark )

Все эти имена классов действительны (поскольку символы $ разрешены в спецификации JVM), и эти имена «безопасны» для генерации компилятором, поскольку определение языка Java рекомендует не использовать символы $ в обычных определениях классов Java.

Разрешение имен в Java еще более усложняется во время выполнения, поскольку полные имена классов уникальны только внутри конкретного экземпляра загрузчика классов . Загрузчики классов упорядочены иерархически, и каждый поток в JVM имеет так называемый контекстный загрузчик классов, поэтому в случаях, когда два разных экземпляра загрузчика классов содержат классы с одинаковым именем, система сначала пытается загрузить класс, используя корневой (или системный) загрузчик классов. а затем спускается по иерархии к загрузчику классов контекста.

Собственный интерфейс Java [ править ]

Java Native Interface , встроенная поддержка методов Java, позволяет программам на языке Java вызывать программы, написанные на другом языке (обычно C или C++). Здесь есть две проблемы с разрешением имен, ни одна из которых не реализована стандартизированным образом :

  • Преобразование имен JVM в собственное имя — это кажется более стабильным, поскольку Oracle делает свою схему общедоступной. [7]
  • Обычное искажение имен C++ — см. выше.

Питон [ править ]

В Python искажение используется для атрибутов класса, которые нежелательно использовать в подклассах. [8] которые обозначаются как таковые путем присвоения им имени с двумя или более начальными подчеркиваниями и не более чем одним завершающим подчеркиванием. Например, __thing будет искалечен, как и ___thing и __thing_, но __thing__ и __thing___ не будет. Среда выполнения Python не ограничивает доступ к таким атрибутам, искажение предотвращает конфликты имен только в том случае, если производный класс определяет атрибут с тем же именем.

При обнаружении искаженных имен атрибутов Python преобразует эти имена, добавляя к началу одно подчеркивание и имя включающего класса, например:

>>> class Test:
...     def __mangled_name(self):
...         pass
...     def normal_name(self):
...         pass
>>> t = Test()
>>> [attr for attr in dir(t) if "name" in attr]
['_Test__mangled_name', 'normal_name']

Паскаль [ править ]

Турбо Паскаль, Delphi [ править ]

Чтобы избежать искажения имен в Паскале, используйте:

exports
  myFunc name 'myFunc',
  myProc name 'myProc';

Бесплатный Паскаль [ править ]

Free Pascal поддерживает перегрузку функций и операторов, поэтому для поддержки этих функций он также использует преобразование имен. С другой стороны, Free Pascal способен вызывать символы, определенные во внешних модулях, созданных на другом языке, и экспортировать свои собственные символы для вызова на другом языке. Для получения дополнительной информации обратитесь к главам 6.2 и 7.1 Руководства программиста Free Pascal .

Фортран [ править ]

Искажение имен также необходимо в компиляторах Фортрана , поскольку язык нечувствителен к регистру . Дальнейшие требования к искажению были наложены позже в ходе развития языка из-за добавления модулей и других функций в стандарт Fortran 90. В частности, искажение регистра является распространенной проблемой, которую необходимо решать при вызове библиотек Фортрана, таких как LAPACK , из других языков, таких C. как

Из-за нечувствительности к регистру имя подпрограммы или функции FOO должен быть преобразован компилятором в стандартизированный регистр и формат, чтобы он был связан одинаково независимо от регистра. Разные компиляторы реализовали это по-разному, и никакой стандартизации не произошло. Компиляторы Fortran для AIX и HP-UX преобразуют все идентификаторы в нижний регистр. foo, а компиляторы Fortran Cray и Unicos преобразовывали идентификаторы в верхний регистр. FOO. Компилятор GNU g77 преобразует идентификаторы в нижний регистр плюс подчеркивание. foo_, за исключением того, что идентификаторы уже содержат подчеркивание FOO_BAR добавить два подчеркивания foo_bar__в соответствии с соглашением, установленным f2c . Многие другие компиляторы, включая от Silicon Graphics (SGI) IRIX компиляторы , GNU Fortran и компилятор Fortran от Intel (кроме Microsoft Windows), преобразуют все идентификаторы в нижний регистр плюс подчеркивание ( foo_ и foo_bar_, соответственно). В Microsoft Windows компилятор Intel Fortran по умолчанию использует верхний регистр без подчеркивания. [9]

Идентификаторы в модулях Fortran 90 необходимо дополнительно изменить, поскольку одно и то же имя процедуры может встречаться в разных модулях. Поскольку стандарт Fortran 2003 требует, чтобы имена процедур модуля не конфликтовали с другими внешними символами, [10] компиляторы обычно используют имя модуля и имя процедуры с отдельным маркером между ними. Например:

module m 
contains
   integer function five()
      five = 5
   end function five
end module m

В этом модуле имя функции будет изменено как __m_MOD_five (например, GNU Фортран), m_MP_five_ (например, ifort от Intel), m.five_ (например, Sun95 от Oracle) и т. д. Поскольку Фортран не позволяет перегружать имя процедуры, а вместо этого использует общие блоки интерфейса и общие процедуры с привязкой к типу, искаженные имена не должны включать в себя подсказки об аргументах.

Параметр BIND Fortran 2003 отменяет любые изменения имен, выполняемые компилятором, как показано выше .

Ржавчина [ править ]

по умолчанию искажаются Имена функций в Rust . Однако это можно отключить с помощью #[no_mangle] атрибут функции. Этот атрибут можно использовать для экспорта функций в C, C++ или Objective-C . [11] Далее, наряду с #[start] атрибут функции или #[no_main] crate, он позволяет пользователю определить точку входа в программу в стиле C. [12]

В Rust используется множество версий схем преобразования символов, которые можно выбрать во время компиляции с помощью -Z symbol-mangling-version вариант. Определены следующие манглеры:

  • legacy Изменение стиля C++ на основе ABI Itanium IA-64 C++. Символы начинаются с _ZN, а хеши имен файлов используются для устранения неоднозначности. Используется начиная с Rust 1.9. [13]
  • v0 Улучшенная версия устаревшей схемы с изменениями для Rust. Символы начинаются с _R. Полиморфизм может быть закодирован. Функции не имеют закодированных типов возвращаемых значений (в Rust нет перегрузки). Имена в Юникоде используют модифицированный Punycode . Сжатие (обратная ссылка) использует байтовую адресацию. Используется начиная с Rust 1.37. [14]

Примеры представлены в Rust. symbol-names тесты. [15]

Objective-C [ править ]

существуют две формы метода По сути, в Objective-C : метод класса («статический») и метод экземпляра . Объявление метода в Objective-C имеет следующую форму:

+ (return-type) name0:parameter0 name1:parameter1 ...
– (return-type) name0:parameter0 name1:parameter1 ...

Методы класса обозначаются знаком «+», методы экземпляра — знаком «-». Типичное объявление метода класса может выглядеть так:

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

С методами экземпляра, выглядящими так:

- (id) value;
- (id) setValue: (id) new_value;

Каждое из этих объявлений метода имеет определенное внутреннее представление. При компиляции каждый метод именуется согласно следующей схеме для методов класса:

_c_Class_name0_name1_ ...

и это, например, методы:

_i_Class_name0_name1_ ...

Двоеточия в синтаксисе Objective-C преобразуются в символы подчеркивания. Итак, метод класса Objective-C + (id) initWithX: (int) number andY: (int) number;, если принадлежит к Point класс переводится как _c_Point_initWithX_andY_и метод экземпляра (принадлежащий одному и тому же классу) - (id) value; перевел бы на _i_Point_value.

Таким образом помечаются каждый из методов класса. Однако поиск метода, на который может реагировать класс, был бы утомительным, если бы все методы были представлены таким образом. Каждому из методов присваивается уникальный символ (например, целое число). Такой символ известен как селектор . В Objective-C можно напрямую управлять селекторами — в Objective-C они имеют определенный тип — SEL.

Во время компиляции строится таблица, отображающая текстовое представление, например _i_Point_value, к селекторам (которым присвоен тип SEL). Управление селекторами более эффективно, чем управление текстовым представлением метода. Обратите внимание, что селектор соответствует только имени метода, а не классу, к которому он принадлежит: разные классы могут иметь разные реализации метода с одним и тем же именем. По этой причине реализациям метода также присваивается определенный идентификатор, известный как указатели реализации, а также тип. IMP.

Отправки сообщений кодируются компилятором как вызовы id objc_msgSend (id receiver, SEL selector, ...) функция или один из ее кузенов, где receiver является получателем сообщения и SEL определяет метод для вызова. Каждый класс имеет свою собственную таблицу, которая сопоставляет селекторы с их реализациями — указатель реализации указывает, где в памяти находится реализация метода. Существуют отдельные таблицы для методов класса и экземпляра. Помимо хранения в SEL к IMP таблицы поиска, функции по существу анонимны.

The SEL значение селектора не различается между классами. Это обеспечивает полиморфизм .

Среда выполнения Objective-C хранит информацию о типах аргументов и возвращаемых значений методов. Однако эта информация не является частью имени метода и может варьироваться от класса к классу.

Поскольку Objective-C не поддерживает пространства имен , нет необходимости изменять имена классов (которые появляются в виде символов в сгенерированных двоичных файлах).

Свифт [ править ]

Swift хранит метаданные о функциях (и многом другом) в искаженных символах, ссылающихся на них. Эти метаданные включают имя функции, атрибуты, имя модуля, типы параметров, тип возвращаемого значения и многое другое. Например:

Искаженное имя метода func calculate(x: int) -> int из MyClass класс в модуле test является _TFC4test7MyClass9calculatefS0_FT1xSi_Si, на Свифт 2014 года. Компоненты и их значения следующие: [16]

  • _T: префикс для всех символов Swift. Все начнется с этого.
  • F: Функция без каррирования.
  • C: Функция класса, т.е. метод
  • 4test: имя модуля с префиксом его длины.
  • 7MyClass: Имя класса, которому принадлежит функция, с префиксом ее длины.
  • 9calculate: имя функции с префиксом ее длины.
  • f: Атрибут функции. В данном случае «f» означает нормальную функцию.
  • S0: Обозначает тип первого параметра (а именно экземпляра класса) как первый в стеке типов (здесь MyClass не является вложенным и поэтому имеет индекс 0).
  • _FT: начинается список типов кортежа параметров функции.
  • 1x: Внешнее имя первого параметра функции.
  • Si: указывает встроенный тип Swift.Int для первого параметра.
  • _Si: Тип возвращаемого значения: снова Swift.Int.

Исправление версий, начиная с Swift 4.0, официально задокументировано. Он сохраняет некоторое сходство с Itanium. [17]

См. также [ править ]

Ссылки [ править ]

  1. ^ «Двоичный интерфейс приложения» . Дланг.орг . Проверено 19 мая 2020 г.
  2. ^ Райнер, Шютце (20 декабря 2017 г.). "Новомодное искажение имени Ди " D Блог Получено 1 мая 2020 г.
  3. ^ Clang — Возможности и цели: совместимость с GCC , 15 апреля 2013 г.
  4. ^ JBIntel_deleted_06032015. «Различия OBJ между компилятором Intel и компилятором VC» . программное обеспечение.intel.com . {{cite web}}: CS1 maint: числовые имена: список авторов ( ссылка )
  5. ^ «Совместимость MSVC» . Проверено 13 мая 2016 г.
  6. ^ «Itanium C++ ABI, раздел 5.1. Внешние имена (также известные как искажение)» . Проверено 16 мая 2016 г.
  7. ^ «Обзор конструкции» . docs.oracle.com .
  8. ^ «PEP 8 — Руководство по стилю для кода Python» .
  9. ^ «Краткий обзор проблем смешанного языка» . Руководство пользователя и справочное руководство по компилятору Intel Fortran 15.0 . Корпорация Интел . Проверено 17 ноября 2014 г.
  10. ^ «Библиотека документации» .
  11. ^ «Интерфейс внешних функций # Вызов кода Rust из C» . Руководство по ржавчине . www.rust-lang.org . Проверено 13 мая 2016 г.
  12. ^ «Нет стандартной библиотеки» . Руководство по ржавчине . www.rust-lang.org . Проверено 13 мая 2016 г.
  13. ^ «rust/src/librustc_codegen_utils/symbol_names/legacy.r.rs по адресу 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · ржавчина-lang/rust · GitHub» . Гитхаб . 3 ноября 2021 г.
  14. ^ «Искажение символа ржавчины» . Книга Rust RFC .
  15. ^ «Rust 1.42.0: src/test/ui/symbol-names» . Гитхаб . Проверено 20 марта 2020 г.
  16. ^ «mikeash.com: Пятничные вопросы и ответы 15 августа 2014 г.: Искажение имен в Swift» . mikeash.com .
  17. ^ "apple/swift: mangling.rst" . Гитхаб . 3 ноября 2021 г.

Внешние ссылки [ править ]

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: ebdda8fe192275529c2a3f8511c7e64a__1714527900
URL1:https://arc.ask3.ru/arc/aa/eb/4a/ebdda8fe192275529c2a3f8511c7e64a.html
Заголовок, (Title) документа по адресу, URL1:
Name mangling - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)