web-dev-qa-db-ja.com

Xcodeで構築された静的ライブラリでのシンボル非表示

エクスポートしたいインターフェイスを除いて、内部のオブジェクトや関数などをすべて隠す静的ライブラリを構築できるかどうかを考えています。 Xcode(gcc 4.2)を実験しています。

このドキュメント に従って、一部のC++クラスで__attribute__((visibility("hidden")))属性を使用しました。また、小さなヘルパーC関数をファイルローカル(静的)などとして定義しました。

ただし、結果の.aライブラリファイルでstringsを実行すると、Release構成でコンパイルした場合でも、表面上隠されているクラスの名前とそのメソッド名、およびファイルローカル関数の名前が表示されます。そこにも。

Gccフラグに-fvisibility=hidden-fno-rttiを追加しました。これにより一部の文字列が削減されますが、クラス名、メソッド名、および静的関数名はすべて、プレーンな形式またはマングルされたが読み取り可能な形式で引き続き存在します。

バイナリにすべての内部的なものの文字列名を出力させることなく、コンパイラにこのようなものを構築させる信頼できる方法はありますか?外部クライアントがリンクしている必要はありません。

(明確にするために、リテラルエクスポートバインディングのニーズに対する内部の名前付けの難読化について質問しています。これらのシンボルが正式にエクスポートされているかどうかに関係なく、stringsコマンドを使用してすべての内部の仕組みが表示されることに戸惑いました。)

ありがとう。

48
Ben Zotto

内部名を非表示にするには、いくつかの単純なXcodeビルド設定が必要です。通常、ソースを変更したり、ビルドされた製品のタイプを変更したりする必要はありません。

  1. 単一オブジェクトのプリリンクを実行して、モジュール間に必要な内部シンボルをすべて削除します。 「Perform Single-Object Prelink」という名前のXcodeビルド設定をYesに設定します(GENERATE_MASTER_OBJECT_FILE = YES)。これにより、ldは「-r」フラグを指定して実行されます。
  2. 設定「ストリップスタイル」が「非グローバルシンボル」(STRIP_STYLE = non-global)に設定されていることを確認します。これにより、「-x」がldに渡されます。
  3. ストリッピングが実際に実行されるのは、後処理が有効になっている(そしてこれがデフォルトではない)場合のみです。 Xcodeビルド設定「Deployment Postprocessing」をyesに設定します。 (DEPLOYMENT_POSTPROCESSING = YES)。また、[個別のストリップを使用]が[はい](常にデフォルトではない)に設定されていることを確認してください(SEPARATE_STRIP = YES)。
  4. ローカルシンボルに加えて、一部のグローバルシンボルを削除する必要がある場合は、Xcodeビルド設定の[追加のストリップフラグ]で、stripコマンドに追加のオプションを指定できます。例えば。私は通常、「-R somefile」オプションを使用して、グローバルシンボルテーブルから削除するシンボルの追加リストをファイルに提供します。
50
bleater

静的ライブラリ内のシンボルを非表示にする主なトリックは、再配置可能なオブジェクトファイルを生成することです(個別の.oファイルのコレクションで構成される静的ライブラリアーカイブとは対照的です)。再配置可能なオブジェクトファイルをビルドするには、XCodeでターゲットをバンドルとして選択する必要があります(「Cocoa Touch Static Library」ではなく)。バンドルターゲットはOS Xテンプレートの下に表示されます。iOS用にビルドする場合は、ビルド設定でターゲットをiOSに設定できます。

ターゲットを正しく設定したら、次の手順でシンボルを正しく隠すことができます。

  1. ビルド設定で[デフォルトで非表示のシンボル]オプションを[はい]に設定します。これにより、ファイルにコンパイルされたすべてのシンボルがプライベートとしてマークされます。

  2. これはライブラリなので、いくつかのシンボルを公開しておく必要があります。公開したい関数のコードを個別のファイルに入れ、_-fvisibility=default_フラグを使用してそれらのファイルをコンパイルする必要があります(このフラグは個々のファイルに「ビルドフェーズ>ソースのコンパイル>-コンパイラフラグ」で設定できます) Xcodeで)。または、表示したい関数/クラスの名前の前に__attribute__((visibility("default")))ディレクティブを付けることができます。

  3. X-codeプロジェクトのリンク設定で、Mach-Oタイプを「Relocatable Object File」に設定します。つまり、すべての.oファイルが再リンクされ、単一のオブジェクトファイルが生成されます。 .oファイルが1つのファイルにリンクされている場合、すべてのシンボルをプライベートとしてマークするのに役立つのはこのステップです。静的ライブラリ(つまり、_.a_ファイル)をビルドする場合、この再リンク手順は発生しないため、シンボルが非表示になることはありません。したがって、ターゲットとして再配置可能オブジェクトファイルを選択することが重要です。

  4. シンボルをプライベートとしてマークした後でも、.oファイルに表示されます。プライベートシンボルを取り除くには、ストリッピングを有効にする必要があります。これは、ビルド設定で「ストリップされたリンクされた製品」設定を「はい」に設定することで実行できます。このオプションを設定すると、オブジェクトファイルからプライベートシンボルを削除する_strip -x_コマンドがオブジェクトファイルで実行されます。

  5. ビルドプロセスによって生成された最終的な再配置可能オブジェクトファイルに対してnmコマンドを実行して、すべての内部シンボルがなくなっていることを再確認します。

上記の手順は、nmコマンドからシンボル名を取り除くのに役立ちます。オブジェクトファイルでstringsコマンドを実行すると、一部の関数名とファイル名が表示されます(一部の文字列とオブジェクト名は例外を介してコンパイルされるため)。私の1つである colleagues には、バイナリセクションを調べてこれらの文字列の名前を変更することにより、これらのシンボルの一部の名前を変更するスクリプトがあります。使用するためにここに掲載しました: https://Gist.github.com/varungulshan/6198167 。このスクリプトをXcodeの追加のビルドステップとして追加できます。

16
Varun Gulshan

以前の回答に基づいてスタティックライブラリのシンボルをLinuxコマンドライン環境から非表示にする方法は少し不明確なので、後世のためにここに私のソリューションを投稿します(これがGoogleの上位の結果の1つであることを前提とします)質問)。

次の2つの.cファイルがあるとします。

// f1.c
const char *get_english_greeting(void)
{
  return "hello";
}

__attribute__((visibility("default")))
const char *get_greeting(void)
{
  return get_english_greeting();
}

そして

// f2.c
#include <stdio.h>
const char *get_english_greeting(void);

__attribute__((visibility("default")))
void print_greeting(void)
{
  puts(get_english_greeting());
}

これら2つのファイルを静的ライブラリに変換して、両方をエクスポートしたいとしますget_greetingおよびprint_greeting だがしかし get_english_greetingライブラリ全体で使用したいため、静的にしたくない場合。

これを実現する手順は次のとおりです。

gcc -fvisibility=hidden -c f1.c f2.c
ld -r f1.o f2.o -o libf.o
objcopy --localize-hidden libf.o
ar rcs libf.a libf.o

これでうまくいきます:

// gcc -L. main.c -lf
void get_greeting(void);
void print_greeting(void);
int main(void)
{
  get_greeting();
  print_greeting();
  return 0;
}

そして、これはしません:

// gcc -L. main.c -lf
const char *get_english_greeting(void);
int main(void)
{
  get_english_greeting();
  return 0;
}

後者の場合、次のエラーが発生します。

/tmp/ccmfg54F.o: In function `main':
main.c:(.text+0x8): undefined reference to `get_english_greeting'
collect2: error: ld returned 1 exit status

それが私たちの望みです。

非表示のシンボル名は静的ライブラリで引き続き表示されますが、リンカーはその静的ライブラリの外部でそれらとのリンクを拒否することに注意してください。シンボル名を完全に削除するには、ストリップして難読化する必要があります。

1
ypsu