Изменение имени

редактировать

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

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

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

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

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

Другое использование искажения имен - это обнаружение дополнительных изменений, не связанных с сигнатурой, таких как чистота функции или то, может ли она потенциально вызвать исключение или запустить сборку мусора. Примером такого языка является D. Это скорее упрощенная проверка ошибок. Например, функции 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будет искажать одно и то же имя, поэтому компоновщик не обнаружит эту проблему, и конечным результатом обычно будет сбой. или повреждение данных или памяти во время выполнения.

Содержание
  • 1 Примеры
    • 1.1 C
    • 1.2 C ++
      • 1.2.1 Простой пример
      • 1.2.2 Сложный пример
      • 1.2.3 Как разные компиляторы изменяют одни и те же функции
      • 1.2.4 Обработка символов C при компоновке из C ++
      • 1.2.5 Стандартизованное изменение имени в C ++
      • 1.2.6 Реальные эффекты изменения имени C ++
      • 1.2.7 Разборка с помощью c ++ filter
      • 1.2.8 Распознавание через встроенный GCC ABI
    • 1.3 Java
      • 1.3.1 Создание уникальных имен для внутренних и анонимных классов
      • 1.3.2 Собственный интерфейс Java
    • 1.4 Python
    • 1.5 Pascal
      • 1.5.1 Диапазон Turbo Pascal / Delphi от Borland
      • 1.5.2 Free Pascal
    • 1.6 Fortran
    • 1.7 Rust
    • 1.8 Objective-C
    • 1.9 Swift
  • 2 См. Также
  • 3 Ссылки
  • 4 Внешние ссылки
Примеры

C

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

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

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

int _cdecl f (int x) {return 0; } int _stdcall g (int y) {возврат 0; } int _fastcall h (int z) {возврат 0; }

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

_f _g @ 4 @ h @ 4

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

В 64-битном соглашении Windows (Microsoft C) нет начального подчеркивания. Это различие может в некоторых редких случаях привести к нерешенным внешним факторам при переносе такого кода на 64-битную версию. Например, код Fortran может использовать 'псевдоним' для связывания с методом 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 ++ также имеет сложные языковые функции, такие как классы, шаблоны, пространства имен и перегрузка операторов, которые изменяют значение определенных символов. на основе контекста или использования. Мета-данные об этих функциях могут быть устранены путем изменения (декорирования) имени символа . Поскольку системы изменения имен для таких функций не стандартизированы для компиляторов, лишь немногие компоновщики могут связывать объектный код, созданный разными компиляторами.

Простой пример

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

int f () {return 1; } int f (int) {возврат 0; } void g () {int я = f (), j = f (0); }

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

int __f_v () {return 1; } int __f_i (int) {возврат 0; } void __g_v () {int я = __f_v (), j = __f_i (0); }

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

Сложный пример

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

пространство имен википедия {статья класса {общедоступная: std :: string format (); // = _ZN9wikipedia7article6formatEv bool print_to (std :: ostream ); // = _ZN9wikipedia7article8print_toERSo class wikilink {public: wikilink (std :: string const name); // = _ZN9wikipedia7article8wikilinkC1ERKSs}; }; }

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

_ZN9wikipedia7article6formatE

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

_ZN9wikipedia7article6formatEv

Для print_toстандартный тип std :: ostream(который является typedef для std :: basic_ostream >), который имеет специальный псевдоним 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
IAR EWARM C ++ 5.4 ARM
GCC 3.x и выше
Clang 1. x и выше
IAR EWARM C ++ 7.4 ARM_Zhi_Zhic_Zhv
GCC 2.9.xh__Fih__Fich__Fv
HP aC ++ A.03.45 PA-RISC
Microsoft Visual C ++ v6-v10 (детализация )? H @@ YAXH @ Z? H @@ YAXHD @ Z? H @@ YAXXZ
Digital Mars C ++
Borland C ++ v3.1@ h $ qi@ h $ qizc@ h $ qv
OpenVMS C ++ v6.5 (режим ARM)H__XIH__XICH__XV
OpenVMS C ++ v6.5 (режим ANSI)CXX $ __ 7H__FIC26CDH77CXX $ __ 7H__FV2CB06E8
OpenVMS C ++ X7.1 IA-64CXHI32 $ _Z26 CXX $ _Z1HIC2NP3LI4CXX $ _Z1HV0BCA19V
SunPro CC__1cBh6Fi_v___1cBh6Fic_v___1cBh6F_v_
__1cBh6F_v_
TruNoC ++ v_
<режим H__C ++ h___
<режим H__C ++ h___
<режим H__C ++ h___ ARM
Tru64 C ++ v6.5 (режим ANSI)__7h__Fi__7h__Fic__7h__Fv
Watcom C ++ 10.6W? H $ n (i) vW? H $ n (ia) vW? H $ n () v

Примечания:

  • Компилятор Compaq C ++ в OpenVMS VAX и Alpha (но не IA-64) и Tru64 имеет две схемы изменения имен. Первоначальная предварительная стандартная схема известна как модель 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 и Clang использует изменение имени Visual C ++ для совместимости.
  • Для компилятора ARM C ++ 7.4 IAR EWARM лучший способ определить имя функции - это выполнить компиляцию с включенным выходом ассемблера и просмотреть выходные данные в сгенерированном таким образом файле ".s".

Обработка Символы C при компоновке из C ++

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

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

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

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

#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]); иначе memset (a, 0, sizeof (a));

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

if (__1cGstrcmp6Fpkc1_i_ (argv [1], «-x») == 0) __1cGstrcpy6Fpcpkc_0_ (a, argv [2]); иначе __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 несовместимы.

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

Реальные эффекты изменения имени C ++

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

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

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

Demangle с помощью c ++ filter

$ c ++ filter -n _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ Map , DefaultAllocator>:: has (StringName const ) const

Demangle через встроенный GCC ABI

#include #include #include 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); бесплатно (demangled_name); возврат 0; }

Вывод:

Demangled: Map , Comparator , DefaultAllocator>:: has (StringName const ) const

Java

В 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 Native Interface

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

  • преобразование JVM в родное имя - это кажется более стабильным, поскольку Oracle делает свою схему общедоступной.
  • Нормальный Изменение имени C ++ - см. Выше.

Python

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

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

>>>class Test (object):... def __mangled_name ( self):... pass... def normal_name (self):... pass>>>t = Test ()>>>[attr для attr в dir (t) if 'name' в attr] ['_Test__mangled_name ',' normal_name ']

Pascal

Диапазон Turbo Pascal / Delphi от Borland

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

экспорт myFunc name' myFunc ', myProc name' myProc ';

Free Pascal

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

Fortran

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

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

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

модуль m содержит целочисленную функцию five () five = 5 end function five end module m

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

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

Rust

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

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

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

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

Objective-C

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

+ (возвращаемый тип) имя 0 : параметр 0 имя 1 : параметр 1... - (тип возврата) имя 0 : параметр 0 имя 1 : параметр 1...

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

+ (id) initWithX: (int) number andY: (int) number; + (id) новый;

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

- (id) значение; - (id) setValue: (id) new_value;

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

_c_Class_name 0 _name 1 _...

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

_i_Class_name 0 _name 1 _...

Двоеточия в синтаксисе 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 (идентификатор получателя, селектор SEL,...)функция или одна из ее кузенов, где получатель- получатель сообщения, а SELопределяет метод звонить. Каждый класс имеет свою собственную таблицу, которая сопоставляет селекторы с их реализациями - указатель реализации указывает, где в памяти находится фактическая реализация метода. Существуют отдельные таблицы для методов класса и экземпляра. Помимо хранения в таблицах поиска от SELдо IMP, функции по существу анонимны.

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

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

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

Swift

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

измененное имя для метода func calculate (x: int) ->intкласса MyClassв модуле testэто _TFC4test7MyClass9calculatefS0_FT1xSi_Siдля Swift 2014 года. Компоненты и их значения следующие:

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

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

См. Также
Ссылки
Внешние ссылки
Последняя правка сделана 2021-05-31 08:52:27
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте