コンパイラ構築において、名前マングリング(名前の装飾とも呼ばれる) は、多くの最新のプログラミング言語でプログラミング エンティティの一意の名前を解決する必要があるために発生するさまざまな問題を解決するために使用される手法です。
関数、構造体、クラス、または別のデータ型の名前に追加された情報をエンコードし、コンパイラからリンカーにさらに意味情報を渡す手段を提供します。
名前マングリングの必要性は、言語が異なるエンティティに同じ識別子で名前を付けることを許可する場合に発生します。ただし、異なるエンティティが異なる名前空間(通常はモジュール、クラス、または明示的な名前空間ディレクティブによって定義される) を占めるか、異なる型シグネチャ(関数オーバーロードなど) を持つ場合に限ります。これらの用途では、シグネチャごとにマシン コードで異なる特殊な呼び出し規約が必要になる可能性があるため、名前マングリングが必要です。
コンパイラによって生成されたオブジェクトコードは通常、リンカーと呼ばれる種類のプログラムによって、他のオブジェクト コード (同じコンパイラまたは別のコンパイラによって生成されたもの) とリンクされます。リンカーは、各プログラム エンティティに関する大量の情報を必要とします。たとえば、関数を正しくリンクするには、関数の名前、引数の数、引数の型などが必要です。
1970 年代のCのような単純なプログラミング言語では、サブルーチンは名前でのみ区別され、パラメータや戻り値の型などの他の情報は無視されていました。C++などの後の言語では、関数のパラメータ型、戻り値の型、呼び出し規約など、ルーチンが「同等」と見なされるためのより厳格な要件が定義されました。これらの要件により、メソッドのオーバーロードと一部のバグの検出が可能になります (異なるソース コードファイルをコンパイルするときに関数の異なる定義を使用するなど)。これらのより厳格な要件は、既存のプログラミング ツールと規約で動作する必要がありました。したがって、追加された要件はシンボルの名前にエンコードされました。これは、従来のリンカーがシンボルについて持っていた唯一の情報だったためです。
例
C
名前マングリングは、 Cや従来のPascalなど、関数のオーバーロードをサポートしていない言語では通常必要とされず、使用されませんが、関数に関する追加情報を提供するために使用する場合もあります。たとえば、Microsoft Windowsプラットフォームを対象とするコンパイラは、さまざまな呼び出し規約をサポートしています。呼び出し規約は、パラメータがサブルーチンに送信され、結果が返される方法を決定します。異なる呼び出し規約は互いに互換性がないため、コンパイラは、特定のルーチンを呼び出すためにどの規約を使用する必要があるかを詳述するコードでシンボルをマングリングします。
Windows のマングリング スキームは Microsoft によって確立され、Digital Mars、Borland、GNU Compiler Collection (GCC) などの他のコンパイラーが Windows プラットフォーム用のコードをコンパイルする際に非公式に従ってきました。 このスキームは、Pascal、D、Delphi、Fortran、C#などの他の言語にも適用されます。 これにより、それらの言語で記述されたサブルーチンは、デフォルトとは異なる呼び出し規約を使用して、 既存の Windowsライブラリを呼び出したり、既存の Windows ライブラリから呼び出されたりすることができます。
次の C の例をコンパイルする場合:
int _cdecl f ( int x ) { 0を返します; } int _stdcall g ( int y ) { 0を返します; } int _fastcall h ( int z ) { 0を返します; }
32 ビット コンパイラはそれぞれ次の出力を生成します。
_f _g@4 @h@4
stdcallおよびマングリング スキームではfastcall、関数はそれぞれ および としてエンコードされます。ここで、X は、パラメータ リスト内の引数のバイト数 (10 進数) です (fastcall の場合はレジスタで渡される引数も含む)。 の場合、関数名の前には単にアンダースコアが付けられます。
_name@X@name@Xcdecl
Windows の 64 ビット規則 (Microsoft C) では、先頭にアンダースコアがありません。この違いにより、このようなコードを 64 ビットに移植するときに、まれに外部が未解決になることがあります。たとえば、Fortran コードは、次のように 'alias' を使用して、名前で C メソッドにリンクできます。
サブルーチンf ()
!DEC$ 属性 C、エイリアス:'_f' :: f
サブルーチン終了
これは 32 ビットでは正常にコンパイルおよびリンクされますが、_f64 ビットでは未解決の外部が生成されます。この問題を回避する方法の 1 つは、「alias」をまったく使用しないことです (C および Fortran では、メソッド名は通常大文字にする必要があります)。もう 1 つの方法は、BIND オプションを使用することです。
サブルーチンf () BIND ( C , NAME = "f" ) END サブルーチン
C では、ほとんどのコンパイラは、翻訳単位内の静的関数と変数 (および C++ では静的と宣言された関数と変数、または匿名名前空間に置かれた関数と変数) も、非静的バージョンと同じマングリング ルールを使用してマングリングします。同じ名前 (および C++ のパラメーター) を持つ関数が別の翻訳単位で定義され使用されている場合、同じ名前にマングリングされ、衝突が発生する可能性があります。ただし、それぞれの翻訳単位で呼び出された場合は、同等にはなりません。コンパイラは通常、これらの関数に対して任意のマングリングを自由に生成できます。これは、他の翻訳単位からこれらの関数に直接アクセスすることは違法であるため、異なるオブジェクト コード間でリンクする必要がまったくないためです (リンクは必要ありません)。リンクの競合を防ぐために、コンパイラは標準のマングリングを使用しますが、いわゆる「ローカル」シンボルを使用します。このような翻訳単位を多数リンクする場合、同じ名前の関数が複数定義される可能性がありますが、結果のコードは、どの翻訳単位からのものかに応じて、どちらか一方のみを呼び出します。これは通常、再配置メカニズムを使用して行われます。
C++
C++コンパイラは、名前マングリングを最も広く使用しています。最初の C++ コンパイラは、Cソース コードへのトランスレータとして実装され、その後 C コンパイラによってオブジェクト コードにコンパイルされました。このため、シンボル名は C 識別子の規則に準拠する必要がありました。その後、マシン コードまたはアセンブリを直接生成するコンパイラが登場しても、システムのリンカーは一般に C++ シンボルをサポートしておらず、マングリングは依然として必要でした。
C ++言語では標準の装飾スキームが定義されていないため、各コンパイラは独自のものを使用します。また、C++には、クラス、テンプレート、名前空間、演算子のオーバーロードなど、コンテキストや使用法に基づいて特定のシンボルの意味を変更する複雑な言語機能もあります。これらの機能に関するメタデータは、シンボルの名前をマングリング (装飾) することで明確にすることができます。このような機能の名前マングリング システムはコンパイラ間で標準化されていないため、異なるコンパイラによって生成されたオブジェクト コードをリンクできるリンカーはほとんどありません。
簡単な例
単一の C++ 翻訳単位では、次の 2 つの関数が定義される場合がありますf()。
int f () { 1を返す; } int f ( int ) { 0を返す; } void g () { int i = f (), j = f ( 0 ); }
これらは名前以外には互いに関係のない、別々の関数です。したがって、C++ コンパイラはシンボル名に型情報をエンコードし、結果は次のようになります。
int __f_v () { 1を返します。} int __f_i ( int ) { 0を返します。} void __g_v () { int i = __f_v (), j = __f_i ( 0 ); }
名前は一意ですが、g()マングル化されています。名前のマングル化は、すべてのC++ シンボルに適用されます (ブロック内のシンボルを除く)。
extern "C"{}
複雑な例
この例のそれぞれの識別子名の下のコメントにあるマングルされたシンボルは、 IA-64 (Itanium) ABIに従って GNU GCC 3.x コンパイラによって生成されたものです。
名前空間wikipedia {クラスarticle { public : std :: string format (); // = _ZN9wikipedia7article6formatEv
bool print_to ( std :: ostream & ); // = _ZN9wikipedia7article8print_toERSo
クラスwikilink { public : wikilink ( std :: string const & name ); // = _ZN9wikipedia7article8wikilinkC1ERKSs }; }; }
すべてのマングルされたシンボルは で始まります_Z(アンダースコアで始まり大文字が続く識別子はC で予約されている識別子Nであるため、ユーザー識別子との競合は回避されます)。ネストされた名前 (名前空間とクラスの両方を含む) の場合は、この後に が続き、次に一連の <長さ、ID> ペア (長さは次の識別子の長さ)、最後にが続きますE。たとえば、は次のようwikipedia::article::formatになります。
_ZN9wikipedia7記事6フォーマットE
関数の場合、この後に型情報が続きます。 はformat()関数の場合void、これは単に ですv。したがって、次のようになります。
_ZN9wikipedia7記事6フォーマットEv
の場合、特別な別名 を持つprint_to標準型std::ostream( の typedef )が使われます。したがって、この型への参照は となり、関数の完全な名前は次のようになります。
std::basic_ostream<char, std::char_traits<char> >SoRSo
_ZN9wikipedia7article8print_toERSo
異なるコンパイラが同じ関数をどのように改変するか
些細な C++ 識別子でさえも修飾する標準化されたスキームは存在せず、その結果、異なるコンパイラ (または同じコンパイラの異なるバージョン、または異なるプラットフォーム上の同じコンパイラ) は、パブリック シンボルを根本的に異なる (したがって完全に互換性のない) 方法で修飾します。異なる C++ コンパイラが同じ関数をどのように修飾するかを考えてみましょう。
注:
- OpenVMS VAXおよびAlpha (IA-64 は除く) とTru64 UNIX上のCompaq C++ コンパイラには、 2 つの名前マングリング スキームがあります。元のプレ標準スキームは ARM モデルとして知られ、C++ Annotated Reference Manual (ARM) で説明されている名前マングリングに基づいています。標準 C++ の新しい機能、特にテンプレートの登場により、ARM スキームはますます不適切になりました。特定の関数型をエンコードできなかったり、異なる関数に対して同じマングリング名が生成されたりしました。そのため、ARM スキームは、ANSI テンプレートのすべての機能をサポートしながらも下位互換性のない、新しい米国規格協会(ANSI) モデルに置き換えられました。
- IA-64 には、標準のアプリケーション バイナリ インターフェイス(ABI) が存在します (外部リンクを参照)。これは、(とりわけ) 標準的な名前マングリング スキームを定義し、すべての IA-64 コンパイラで使用されます。GNU GCC 3.xでは、この標準で定義された名前マングリング スキームをさらに採用し、Intel 以外の他のプラットフォームでも使用しています。
- Visual Studio
undnameおよび Windows SDK には、指定されたマングル名に対して C スタイルの関数プロトタイプを出力するプログラムが含まれています。 - Microsoft Windowsでは、Intelコンパイラ[2]とClang [3]は互換性のために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++ コンパイラの相互運用性を保証するのに十分ではなく、相互運用が可能で安全であるという誤った印象を与える可能性もありますが、実際にはそうではありません。名前マングリングは、C++ 実装で決定および遵守する必要があるいくつかのアプリケーション バイナリ インターフェイス(ABI) の詳細のうちの 1 つにすぎません。例外処理、仮想テーブルレイアウト、構造、スタック フレームパディングなどの他の ABI の側面も、異なる C++ 実装の非互換性の原因となります。さらに、特定の形式のマングリングを要求すると、実装の制限 (シンボルの長さなど) によって特定のマングリング スキームが決定されるシステムで問題が発生します。名前マングリングの標準化された要件は、マングリングがまったく必要ない実装 (C++ 言語を理解するリンカーなど) を防ぐことにもなります。
したがって、 C ++ 標準では、名前マングリングを標準化しようとはしません。逆に、『Annotated C++ Reference Manual』 ( ARMとも呼ばれ、ISBN 0-201-51459-1、セクション 7.2.1c) では、ABI の他の側面に互換性がない場合にリンクを防止するために、異なるマングリング スキームの使用を積極的に推奨しています。
ただし、上記のセクションで詳述したように、一部のプラットフォーム[4]では、名前のマングリングを含む完全なC++ ABIが標準化されています。
C++ の名前マングリングの実際の影響
C++ シンボルはDLLや共有オブジェクトファイルから定期的にエクスポートされるため、名前修飾スキームは単にコンパイラ内部の問題ではありません。異なるコンパイラ (または多くの場合、同じコンパイラの異なるバージョン) は、異なる名前修飾スキームでそのようなバイナリを生成します。つまり、ライブラリの作成に使用されたコンパイラとそれを使用するプログラムが異なるスキームを採用している場合、シンボルが解決されないことがよくあります。たとえば、複数の C++ コンパイラ (GNU GCC と OS ベンダーのコンパイラなど) がインストールされているシステムでBoost C++ Libraries をインストールする場合、複数回コンパイルする必要があります (GCC 用に 1 回、ベンダー コンパイラ用に 1 回)。
互換性のないオブジェクト コード (クラスや例外など、異なる ABI に基づくコード) を生成するコンパイラが異なる名前マングリング スキームを使用することは、安全性の観点から好ましいことです。これにより、これらの非互換性がソフトウェアの実行時ではなくリンク フェーズで検出されることが保証されます (実行時には、不明瞭なバグや深刻な安定性の問題が発生する可能性があります)。
このため、名前の装飾は C++ 関連のABIにとって重要な側面です。
特に大規模で複雑なコード ベースでは、リンカー エラー メッセージ内で出力されたマングル名をソース内の特定の対応するトークン/変数名にマッピングすることが困難または非現実的な場合があります。この問題により、1 つのコンパイラとリンカーしか使用されていない場合でも、ビルド エンジニアまたはテスト エンジニアが関連するソース ファイルを識別することが非常に困難になる可能性があります。デマングラ (リンカー エラー報告メカニズム内のものを含む) が役立つ場合もありますが、マングル メカニズム自体が重要な曖昧性解消情報を破棄する場合があります。
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 ( "デマングル済み: %s \n " , demangled_name ); free ( demangled_name ); return 0 ; }
出力:
Demangled: Map<StringName, Ref<GDScript>, Comparator<StringName>, DefaultAllocator>::has(StringName const&) const
ジャワ
Javaでは、メソッドまたはクラスのシグネチャには、その名前と、該当する場合はメソッド引数と戻り値の型が含まれます。言語、コンパイラ、および .class ファイル形式はすべて一緒に設計されているため (そして最初からオブジェクト指向と普遍的な相互運用性を念頭に置いていたため)、シグネチャの形式は文書化されています。
内部クラスと匿名クラスに一意の名前を作成する
匿名クラスのスコープはその親クラスに限定されるため、コンパイラは内部クラスに対して「修飾された」パブリック名を生成し、同じ名前空間に同じ名前の他のクラス (内部クラスまたは非内部クラス) が存在する場合の競合を回避する必要があります。同様に、匿名クラスには「偽の」パブリック名を生成する必要があります (匿名クラスの概念はコンパイラにのみ存在し、ランタイムには存在しないため)。したがって、次の Java プログラムをコンパイルすると、
パブリッククラスfoo {クラスbar {パブリックint x ; }
パブリックvoid zark () {オブジェクトf = new Object () {パブリックString toString () {戻り値"hello" ; } } ; } }
3 つの.classファイルが生成されます。
- foo.class、メイン(外部)クラスfooを含む
- foo$bar.class、名前付き内部クラスfoo.barを含む
- foo$1.class、匿名内部クラス(メソッドfoo.zarkに対してローカル)を含む
これらのクラス名はすべて有効です ($ 記号は JVM 仕様で許可されているため)。また、Java 言語定義では通常の Java クラス定義で $ 記号を使用しないように推奨されているため、これらの名前はコンパイラが生成しても「安全」です。
Java での名前解決は実行時にさらに複雑になります。クラスの完全修飾名は特定のクラスローダーインスタンス内でのみ一意だからです。クラスローダーは階層的に順序付けられ、JVM 内の各スレッドにはいわゆるコンテキスト クラス ローダーがあります。そのため、2 つの異なるクラスローダー インスタンスに同じ名前のクラスが含まれている場合、システムは最初にルート (またはシステム) クラスローダーを使用してクラスをロードしようとし、次に階層を下ってコンテキスト クラス ローダーに進みます。
Javaネイティブインターフェース
Java のネイティブ メソッド サポートであるJava Native Interface を使用すると、Java 言語プログラムから別の言語 (通常は C または C++) で記述されたプログラムを呼び出すことができます。ここでは、名前解決に関する 2 つの問題がありますが、どちらも標準化された方法で実装されていません。
- JVMからネイティブ名への変換 - Oracleがそのスキームを公開しているため、これはより安定しているようです。[5]
- 通常の C++ の名前マングリング - 上記を参照してください。
パイソン
Pythonでは、マングリングはサブクラスで使用したくないクラス属性に対して使用されます[6]。マングリングは、名前の先頭に 2 つ以上のアンダースコアを付け、末尾に 1 つ以下のアンダースコアを付けることで、そのように指定されます。たとえば、__thingはマングリングされますが、 や___thingはマングリングされます__thing_が、 と__thing__は__thing___マングリングされません。Python のランタイムはこのような属性へのアクセスを制限しません。マングリングは、派生クラスが同じ名前の属性を定義した場合の名前の衝突を防ぐだけです。
名前がマングルされた属性に遭遇すると、Python はこれらの名前の前に 1 つのアンダースコアとそれを囲むクラスの名前を追加して変換します。次に例を示します。
>>>クラス Test :
... def __mangled_name ( self ):
... 合格
... def normal_name ( self ):
... 合格
>>> t = Test ()
>>> [ attr for attr in dir ( t ) if "name" in attr ]
['_Test__mangled_name', 'normal_name']
パスカル
ターボパスカル、デルファイ
Pascal で名前のマングリングを回避するには、次を使用します。
myFunc name 'myFunc' 、myProc name 'myProc'をエクスポートします。
フリーパスカル
Free Pascal は関数と演算子のオーバーロードをサポートしているため、これらの機能をサポートするために名前マングリングも使用します。一方、Free Pascal は、別の言語で作成された外部モジュールで定義されたシンボルを呼び出したり、独自のシンボルをエクスポートして別の言語で呼び出すことができます。詳細については、Free Pascal プログラマーズ ガイドの第 6.2 章および第 7.1 章を参照してください。
フォートラン
名前のマングリングはFortranコンパイラでも必要です。これはもともと言語が大文字と小文字を区別しないためです。言語の進化の過程で、Fortran 90 標準にモジュールやその他の機能が追加されたため、さらにマングリングの要件が課されました。特に大文字と小文字のマングリングは、 Cなどの他の言語からLAPACKなどの Fortran ライブラリを呼び出すときに対処しなければならない一般的な問題です。
大文字と小文字を区別しないため、サブルーチンまたは関数の名前は、FOO大文字と小文字に関係なく同じ方法でリンクされるように、コンパイラによって標準化された大文字と小文字および形式に変換される必要があります。 さまざまなコンパイラがさまざまな方法でこれを実装しており、標準化は行われていません。AIXおよびHP-UX Fortran コンパイラは、すべての識別子を小文字 に変換しますfooが、CrayおよびUnicos Fortran コンパイラは、識別子をすべて大文字 に変換しましたFOO。GNU g77コンパイラは、識別子を小文字とアンダースコア に変換しますfoo_。ただし、すでにアンダースコアを含む識別子には、 f2cによって確立された規則に従って、FOO_BAR2 つのアンダースコアが追加されます。 Silicon Graphics (SGI) のIRIXコンパイラ、GNU Fortran、およびIntelの Fortran コンパイラ (Microsoft Windows を除く)を含む他の多くのコンパイラは、すべての識別子を小文字とアンダースコア (それぞれと)に変換します。 Microsoft Windows では、Intel Fortran コンパイラはデフォルトで下線なしの大文字になります。[7]foo_bar__foo_foo_bar_
Fortran 90 モジュールの識別子は、異なるモジュールに同じプロシージャ名が出現する可能性があるため、さらにマングルする必要があります。Fortran 2003 標準では、モジュール プロシージャ名が他の外部シンボルと競合しないようにする必要があるため、[8]コンパイラは、モジュール名とプロシージャ名を、間に明確なマーカーを付けて使用する傾向があります。例:
モジュールmには整数関数5 ()が含まれています。5 = 5終了関数5終了モジュールm
このモジュールでは、関数の名前は__m_MOD_five(例: GNU Fortran)、m_MP_five_(例: Intel の ifort)、m.five_(例: Oracle の sun95) のようにマングルされます。Fortran ではプロシージャ名のオーバーロードは許可されませんが、代わりに汎用インターフェイス ブロックと汎用型バインド プロシージャが使用されるため、マングルされた名前に引数に関する手がかりを組み込む必要はありません。
Fortran 2003 BIND オプションは、上記のようにコンパイラによって行われた名前のマングリングをオーバーライドします。
さび
Rustでは、関数名はデフォルトでマングルされます。ただし、これはfunction属性によって無効にすることができます。この属性は、関数をC、C++、またはObjective-C#[no_mangle]にエクスポートするために使用できます。[9]さらに、 function属性またはcrate属性とともに、ユーザーはプログラムのCスタイルのエントリポイントを定義することができます。[10]#[start]#[no_main]
Rust では、コンパイル時にオプションで選択できるシンボルマングリング スキームの多くのバージョンが使用されています-Z symbol-mangling-version。次のマングラーが定義されています。
legacyItanium IA-64 C++ ABIに基づいたC++スタイルのマングリング。シンボルは で始まり_ZN、ファイル名のハッシュは曖昧さ回避のために使用されます。Rust 1.9以降で使用されています。[11]v0レガシースキームの改良版で、Rust用に変更されています。シンボルは で始まります_R。ポリモーフィズムをエンコードできます。関数の戻り値の型はエンコードされません (Rust にはオーバーロードがありません)。Unicode 名は修正されたpunycodeを使用します。圧縮 (後方参照) はバイトベースのアドレス指定を使用します。Rust 1.37 以降で使用されています。[12]
Rustsymbol-namesテストに例が示されています。[13]
オブジェクティブC
Objective-Cには基本的にクラス("静的") メソッドとインスタンス メソッドの 2 つの形式のメソッドが存在します。Objective-C でのメソッド宣言は次の形式になります。
+ (戻り値の型)名前0 :パラメーター0 名前1 :パラメーター1 ... – (戻り値の型)名前0 :パラメーター0 名前1 :パラメーター1 ...
クラス メソッドは + で示され、インスタンス メソッドは - で示されます。典型的なクラス メソッドの宣言は次のようになります。
+ ( id ) initWithX: ( int )数値andY: ( int )数値; + ( id ) new ;
インスタンスメソッドは次のようになります。
- ( id ) 値;
- ( id ) setValue: ( id )新しい値;
これらのメソッド宣言にはそれぞれ固有の内部表現があります。コンパイルされると、各メソッドは次のクラス メソッドのスキームに従って名前が付けられます。
_c_クラス_名前0 _名前1 _ ...
たとえば、次のメソッドがあります。
_i_クラス_名前0 _名前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。
メッセージ送信は、関数またはその類似関数の 1 つへの呼び出しとしてコンパイラによってエンコードされます。ここで、 はメッセージの受信者であり、呼び出すメソッドを決定します。各クラスには、セレクタをその実装にマップする独自のテーブルがあります。実装ポインタは、メソッドの実装がメモリ内のどこにあるかを指定します。クラス メソッドとインスタンス メソッドには、別々のテーブルがあります。関数は、ルックアップ テーブルに格納される以外は、基本的に匿名です。
id objc_msgSend (id receiver, SEL selector, ...)receiverSELSELIMP
SELセレクターの値はクラス間で変化しません。これにより、ポリモーフィズムが可能になります。
Objective-C ランタイムは、メソッドの引数と戻り値の型に関する情報を保持します。ただし、この情報はメソッド名の一部ではなく、クラスごとに異なる場合があります。
Objective-C は名前空間をサポートしていないため、クラス名 (生成されたバイナリではシンボルとして表示されます) をマングリングする必要はありません。
迅速
Swift は、関数 (およびその他) に関するメタデータを、それらを参照するマングルされたシンボル内に保持します。このメタデータには、関数の名前、属性、モジュール名、パラメータの型、戻り値の型などが含まれます。例:
モジュール内のクラスfunc calculate(x: int) -> intのメソッドのマングル名は、2014 Swiftではです。構成要素とその意味は次のとおりです。[14]MyClasstest_TFC4test7MyClass9calculatefS0_FT1xSi_Si
_T: すべての Swift シンボルのプレフィックス。すべてがこれで始まります。F: 非カリー化関数。C: クラスの関数、つまりメソッド4test: モジュール名。長さがプレフィックスとして付きます。7MyClass: 関数が属するクラスの名前。先頭にその長さが付きます。9calculate: 関数名。長さが先頭に付きます。f: 関数属性。この場合は「f」で、通常の関数を意味します。S0: 最初のパラメータの型 (つまりクラス インスタンス) を型スタックの最初の型として指定します (ここではMyClassネストされていないため、インデックスは 0 になります)。_FT: 関数のパラメータタプルの型リストが始まります。1x: 関数の最初のパラメータの外部名。Si: 最初のパラメータとして組み込みの Swift 型 Swift.Int を示します。_Si: 戻り値の型: 再び Swift.Int。
Swift 4.0以降のバージョンのマングリングは公式に文書化されており、Itaniumとの類似点がいくつか残っています。[15]
参照
- アプリケーション プログラミング インターフェイス(API)
- アプリケーションバイナリインターフェース(ABI)
- 呼び出し規約
- アプリケーション仮想化ソフトウェア(VM)の比較
- 外部関数インターフェース(FFI)
- Java ネイティブ インターフェース(JNI)
- 言語バインディング
- ストロッピング
- スウィグ
参考文献
- ^ Clang - 機能と目標: GCC 互換性、2013 年 4 月 15 日
- ^ JBIntel_deleted_06032015. 「Intel コンパイラと VC コンパイラの OBJ の違い」。software.intel.com。
{{cite web}}: CS1 maint: numeric names: authors list (link) - ^ 「MSVC 互換性」 。2016年5 月 13 日閲覧。
- ^ "Itanium C++ ABI、セクション 5.1 外部名 (別名マングリング)" . 2016 年5 月 16 日に取得。
- ^ 「設計の概要」。docs.oracle.com。
- ^ 「PEP 8 - Python コードのスタイル ガイド」。
- ^ 「混合言語の問題の概要」。Intel Fortran コンパイラ 15.0 のユーザーおよびリファレンス ガイド。Intel Corporation。2014年11 月 17 日閲覧。
- ^ 「ドキュメントライブラリ」。
- ^ 「Foreign Function Interface # Calling Rust code from C」. Rust マニュアル. rust-lang.org . 2016 年5 月 13 日閲覧。
- ^ 「No stdlib」。Rustマニュアル。rust-lang.org。2016年5 月 13 日閲覧。
- ^ “rust/src/librustc_codegen_utils/symbol_names/legacy.r.rs at 57e1da59cd0761330b4ea8d47b16340a78eeafa9 · rust-lang/rust · GitHub”. GitHub . 2021年11月3日.
- ^ 「Rust シンボルのマングリング」。Rust RFC ブック。
- ^ “Rust 1.42.0: src/test/ui/symbol-names”. Github . 2020年3月20日閲覧。
- ^ 「mikeash.com: Friday Q&A 2014-08-15: Swift の名前のマングリング」。mikeash.com。
- ^ 「apple/swift: mangling.rst」。GitHub 。 2021年11月3日。
外部リンク
- 名前マングリング スキームを含む、C++ 用の Linux Itanium ABI。
- Macintosh C/C++ ABI 標準仕様
- c++filt – GNU/Intel コンパイラ用にエンコードされた C++ シンボルをデマングルするフィルター
- undname – 名前をデマングルするための msvc ツール。
- demagnger.com – GCC および MSVC C++ シンボルをデマングルするためのオンライン ツール
- Objective-C ランタイム システム – Apple のObjective-C プログラミング言語 1.0より
- Agner Fogによる「Calling Conventions for different C++compilers」には、さまざまな x86 および x64 C++ コンパイラの名前マングリング スキームの詳細な説明が含まれています (2011-06-08 バージョンの 24 ~ 42 ページ)
- C++ 名前マングリング/デマングリング Visual C++ コンパイラの名前マングリング スキームの詳細な説明
- PHP UnDecorateSymbolName は、Microsoft Visual C の関数名をデマングルする PHP スクリプトです。
- C と C++ コードの混在
- Levine, John R. (2000) [1999 年 10 月]。「第 5 章: シンボル管理」。リンカーとローダー。Morgan Kaufmann シリーズ ソフトウェア エンジニアリングおよびプログラミング (第 1 版)。サンフランシスコ、米国: Morgan Kaufmann。ISBN 1-55860-496-0. OCLC 42413382. 2012年12月5日にオリジナルからアーカイブ。2020年1月12日閲覧。コード: [1][2] 訂正: [3]
- フィボス・ケファロニティスが解明した名前の改変
