3

最近、コンパイラ (MSVC10) で RTTI を無効にしたところ、実行可能ファイルのサイズが大幅に減少しました。テキスト エディターを使用して生成された実行可能ファイルを比較すると、RTTI のないバージョンではシンボル名がはるかに少なく、スペースが節約されていることがわかりました。

私の知る限り、これらのシンボル名はtype_info、各ポリモーフィック型に関連付けられた構造を埋めるためにのみ使用され、 type_info::name().

標準によれば、 によって返される文字列の形式は規定されtype_info::name()ていません。つまり、深刻なことを移植可能に行うために、誰もそれに頼ることはできません。したがって、実装は何も壊さずに常に空の文字列を返すことができるはずです。したがって、RTTI サポートを無効にすることなく実行可能サイズを縮小できます (したがって、typeidoperator & comparetype_infoのオブジェクトを安全に使用できます)。

しかし...それは可能ですか?私は MSVC10 を使用していますが、それを行うオプションが見つかりませんでした。RTTI を完全に無効にする ( /GR-) か、完全なタイプ名で有効にする( ) ことができます/GR。そのようなオプションを提供するコンパイラはありますか?

4

3 に答える 3

4

したがって、実装は何も壊さずに常に空の文字列を返すことができるはずです。したがって、RTTI サポートを無効にすることなく実行可能サイズを縮小できます (したがって、typeid 演算子を使用して type_info のオブジェクトを安全に比較できます)。

あなたは基準を読み違えています。(null で終わるバイナリ文字列以外の) 未指定からの戻り値を作成する意図はtype_info::name()、コンパイラ/ライブラリ/ランタイム環境の実装者に、RTTI 要件を最適な方法で実装する自由を与えることでした。プログラマーであるあなたは、Application Binary Interface (存在する場合) がどのように設計または実装されるかについて、発言することはできません。

于 2012-07-09T21:31:22.593 に答える
3

ここで 3 つの異なる質問をしています。

  1. 最初の質問は、MSVC が名前を生成しないようにする方法があるかどうか、または他のコンパイラで可能かどうか、またはそれが失敗した場合、問題を壊さずに生成された type_info から名前を取り除く方法があるかどうかを尋ねます。

  2. 次に、名前を削除できるように、MS ABI を変更できるかどうかを知りたいと考えています (おそらくあまり過激ではありません)。

  3. 最後に、名前のない ABI を設計できるかどうかを知りたいと考えています。

質問 1 自体が複雑な質問です。私の知る限り、MSVC に名前を生成させないようにする方法はありません。また、他のほとんどのコンパイラは、 typeid(foo).name() が返す必要があるものを明確に定義する ABI を対象としているため、名前を生成しないようにすることもできません。

さらに興味深いのは、名前を削除するとどうなるかということです。MSVC については、答えがわかりません。ここで行う最善の方法は、おそらく試してみることです。DLL に移動し、各名前の最初の文字を \0 に変更して、dynamic_cast が壊れるかどうかなどを確認します (Mac および Linux の x86_64 実行可能ファイルでこれを実行できることはわかっています)。 g++ 4.2 によって生成され、動作しますが、今は脇に置きましょう。)

質問 2 については、名前の空白化が機能しないと仮定すると、名前ベースのシステムを名前を必要としないように変更することはそれほど難しくありません。些細な解決策の 1 つは、名前のハッシュ、または ROT13 でエンコードされた名前を使用することです (ここでの本来の目的は、「私のクラスの恥ずかしい名前をカジュアルなユーザーに見られたくない」ということを思い出してください)。しかし、それがあなたが探しているものに当てはまるかどうかはわかりません。もう少し複雑なソリューションは次のとおりです。

  • 「dllexport」されたクラスの場合、UUID を生成し、それを typeinfo に入れ、DLL と共に生成される .LIB インポート ライブラリにも入れます。
  • 「dllimport」されたクラスの場合、.LIB から UUID を読み取り、代わりにそれを使用します。

したがって、dllexport/dllimport を正しく取得できれば、exe は dll と同じ UUID を使用するため、機能します。しかし、そうしないとどうなりますか?1 つを dllexport としてマークし、もう 1 つを dllimport としてマークせずに、DLL と EXE で "誤って" 同一のクラス (たとえば、同じパラメーターを使用した同じテンプレートのインスタンス化) を指定した場合はどうなるでしょうか? RTTI はそれらを同じタイプとして認識しません。

これは問題ですか?まあ、C++ 標準はそうは言っていません。また、MS のドキュメントもありません。実際、ドキュメントには、これを行うことは許可されていないと明示的に記載されています。あるモジュールから明示的にエクスポートして別のモジュールにインポートしない限り、2 つの異なるモジュールで同じクラスまたは関数を使用することはできません。これがクラス テンプレートで行うのが非常に難しいという事実は問題であり、彼らが解決しようとしていない問題です。

現実的な例を見てみましょうlinkedlist。すべてのリストの最後のノードがそのセンチネルを指し、end() 関数がそれへのポインターを返すだけの、グローバルな静的センチネルを持つノードベースのクラス テンプレートを作成します。(マイクロソフト独自の std::map の実装は、これを正確に行うために使用されていました。それがまだ正しいかどうかはわかりません。) linkedlist<int>exe で a を新規作成し、それを dll 内の関数に参照渡ししますl.begin()l.end(). exeによって作成されたノードはどれもdll内のセンチネルのコピーを指していないため、決して終了しません。もちろん、自分自身を渡す代わりに DLL にl.begin()andを渡すと、この問題は発生しません。あなたは通常、を渡すことで逃げることができますl.end()lstd::stringまたは、壊れるものに依存しないという理由だけで、参照によって他のさまざまなタイプ。しかし、実際にそうすることが許されているわけではありません。運が良かっただけです。したがって、名前をリンク時に検索する必要がある UUID に置き換えることは、リンクローダー時に型を照合できないことを意味しますが、リンクローダー時に型を照合できないという事実は、これが無関係。

これらの問題のない名前ベースのシステムを構築することは可能です。ARM C++ ABI (およびそれに基づく iOS および Android ABI) は、プログラマーが MS よりもはるかに少ない費用で実現できるものを制限しており、リンクローダーがそれを機能させる方法について非常に具体的な要件があります (3.2.5)。これは、次のように設計で明示的に選択されていたため、名前ベースでないように変更できませんでした。

• type_info::operator== と type_info::operator!= は、RTTI オブジェクトへのポインターとその名前だけでなく、type_info::name() によって返される文字列を比較します。

• type_info::name() によって返されるアドレスには依存しません。(つまり、t1.name() != t2.name() は t1 != t2 を意味するものではありません)。

最初の条件では、これらの演算子 (および type_info::before()) をアウトオブラインで呼び出す必要があり、実行環境がそれらの適切な実装を提供する必要があります。

しかし、この問題がなく、名前を使用しない ABI を構築することも可能です。これは#3にうまく続きます。

Itanium ABI (とりわけ、x86_64 および i386 上の OS X と最近の Linux の両方で使用される) はlinkedlist<int>、あるオブジェクトで生成されたオブジェクトと、linkedlist<int>別のオブジェクトで同じヘッダーから生成されたオブジェクトを実行時にリンクできることを保証します。つまり、同じ type_info オブジェクトを持つ必要があります。2.9.1 から:

2 つの type_info ポインターが等しい場合にのみ、2 つの type_info ポインターが同等の型記述を指すように意図されています。実装は、たとえば、シンボル プリエンプション、COMDAT セクション、またはその他のメカニズムを使用して、この制約を満たす必要があります。

コンパイラ、リンカー、およびリンクローダーが連携して、実行可能ファイルで作成された が、共有オブジェクトで作成されたlinkedlist<int>とまったく同じ type_info オブジェクトを指すようにする必要があります。linkedlist<int>

したがって、すべての名前を削除しても、まったく違いはありません。(そして、これは非常に簡単にテストおよび検証できます。)

しかし、どうすればこの ABI 仕様を実装できるでしょうか? j_kubik は、リンク時の情報を .so ファイルに保存する必要があるため、事実上不可能であると主張しています。これは明らかな答えを示しています。リンク時の情報を .so ファイルに保存します。実際、ロード時の再配置などを処理するために、すでにそれを行う必要があります。これは、保存する必要があるものを拡張するだけです。実際、Apple も GNU/linux/g++/ELF もまさにそれを行っています。(これは、複雑な Linux システムを構築するすべての人が、数年前にシンボルの可視性と漠然としたリンケージについて学ばなければならなかった理由の一部です。)

この問題を解決するには、さらに明白な方法があります。C++ コンパイラとリンカーを連携させて C ベースのリンク ローダーをだますのではなく、C++ ベースのリンク ローダーを作成します。しかし、私の知る限り、Be 以来、誰もそれを試みていません。

于 2012-07-10T23:47:07.167 に答える
0

タイプ記述子の要件:

  • マルチ コンパイル ユニットおよび共有ライブラリ環境で正しく動作します。
  • さまざまなバージョンの共有ライブラリで正しく動作します。
  • 異なるコンパイル単位は名前を除いて型に関する情報を共有しませんが、正常に動作します。通常、すべてのコンパイル単位で同じ型を定義するために 1 つのヘッダーが使用されますが、必須ではありません。場合でも、結果のオブジェクト ファイルには影響しません。
  • テンプレートのインスタンス化は、それらを使用するすべてのライブラリで完全に定義する必要がある (つまり、type_info データを含む) 必要があるという事実にもかかわらず、正しく動作し、そのようなライブラリがいくつか一緒に使用されている場合でも、1 つの型のように動作します。

4 番目の規則は、UUID のような名前に基づかないすべての型記述子を基本的に禁止します (型定義で具体的に言及されていない限り、それはせいぜい名前の置換であり、おそらく標準の変更が必要です)。

したがって、suggeste .LIB ファイルなどの別のファイルで UUID をストーミングすることも問題を引き起こします。新しいタイプを実装する異なるライブラリ バージョンが問題を引き起こす可能性があります。

type_infoコンパイル ユニットは、リンカーを使用する必要なく、同じ型 (およびその ) を共有できる必要があります。これは、言語固有の問題がないためです。

そのため、type-name は、コンパイルとリンクを完全に再モデル化しなくても (動的にも)、一意の型記述子のみにすることができます。私はそれが機能していると想像できましたが、現在のスキームではそうではありません。

于 2012-07-10T02:03:12.930 に答える