GCCとldで未使用のC / C ++シンボルを削除する方法は?


110

実行可能ファイルのサイズを大幅に最適化する必要があります(ARM開発)と、現在のビルドスキーム(gcc+ ld)で未使用のシンボルが削除されないことに気付きました。

arm-strip --strip-unneeded結果の実行可能ファイル/ライブラリのの使用は、実行可能ファイルの出力サイズを変更しません(私には理由がわかりません、たぶんそれができないのかもしれません)

使用されていないシンボルが結果のファイルから削除されるように、ビルドパイプラインを変更する方法(存在する場合)は何ですか?


私もこれについては考えていませんが、現在の組み込み環境は非常に「強力」ではなく500K2M結果を保存しても、ロードパフォーマンスが非常に向上します。

更新:

残念ながら、gcc私が使用する現在のバージョンには-dead-stripオプションがなく、-ffunction-sections... + --gc-sectionsfor を使用してもld、結果の出力に大きな違いはありません。

gcc + ld未使用のシンボルを自動的に取り除く必要があると確信していたので、これが問題になることにもショックを受けました(なぜそれらを保持する必要があるのですか?)。


シンボルが使用されていないことをどうやって知っていますか?
zvrba

どこも参照されていない=>最終的なアプリケーションで使用されていない。コンパイル/リンク中にコールグラフを作成することはそれほど難しくないと思います。
Yippie-Ki-Yay 2011

1
デッドシンボルを削除して.oファイルのサイズを削減しようとしていますか、それとも実行可能メモリにロードされた後の実際のコードフットプリントのサイズを削減しようとしていますか?あなたが「埋め込み」と言っているという事実は後者を示唆しています。あなたが尋ねる質問は前者に焦点を当てているようです。
Ira Baxter、

@Ira出力実行可能ファイルのサイズを縮小しようとしています。たとえばboostライブラリを使用する既存のアプリケーションを移植しようとすると、結果の.exeファイルに多くの未使用のオブジェクトファイルが含まれ、現在の埋め込みランタイムの仕様が原因です。 、10mbアプリケーションの起動には、たとえば500kアプリケーションの起動よりもはるかに時間がかかります。
Yippie-Ki-Yay

8
@Yippie:ロード時間を最小限に抑えるためにコードを削除したい。あなたが取り除きたいコードは未使用のメソッドなどです。ライブラリから。はい、これを行うにはコールグラフを作成する必要があります。それはそれほど簡単ではありません。それはグローバルコールグラフでなければならず、保守的(使用される可能性のあるものを削除できない)で、正確でなければなりません(そのため、理想的なコールグラフに近いので、何がそうでないかを本当に理解できます)中古)。大きな問題は、グローバルで正確なコールグラフを実行することです。リンカはもちろん、これを行う多くのコンパイラを知らない。
Ira Baxter

回答:


131

GCCの場合、これは2つの段階で行われます。

最初にデータをコンパイルしますが、コードを変換単位内の個別のセクションに分割するようコンパイラーに指示します。これは、次の2つのコンパイラフラグを使用して、関数、クラス、および外部変数に対して行われます。

-fdata-sections -ffunction-sections

リンカー最適化フラグを使用して翻訳単位をリンクします(これにより、リンカーは参照されていないセクションを破棄します)。

-Wl,--gc-sections

したがって、2つの関数が宣言されているtest.cppというファイルが1つあり、そのうちの1つが未使用である場合、gcc(g ++)への次のコマンドで未使用のファイルを省略できます。

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections

(-Osは、GCCにサイズを最適化するように指示する追加のコンパイラフラグです)


3
これにより、GCCのオプションの説明(テスト済み)に従って実行可能ファイルが遅くなることに注意してください。
変態

1
これを使用mingwすると、静的にlibstdc ++とlibgccをフラグでリンクすると機能しません-static。リンカーオプション-strip-allはかなり役立ちますが、生成される実行可能ファイル(またはdll)は、Visual Studioが生成するものよりも約4倍大きくなります。ポイントは、私はどのようlibstdc++にコンパイルされたかを制御できないことです。ld唯一のオプションがあるはずです。
Fabio 2017

34

このスレッド信頼できる場合は、-ffunction-sectionsおよび-fdata-sectionsを指定する必要があります。これにより、各関数とデータオブジェクトが独自のセクションに配置されます。次に--gc-sections、GNU ldを使用して、未使用のセクションを削除します。


6
@MSalters:CおよびC ++標準に違反しているため、デフォルトではありません。突然グローバルな初期化が行われなくなり、非常に驚​​いたプログラマーがいます。
Ben Voigt、2011

1
@MSalters:デフォルトの動作にすることを提案した非標準の動作違反オプションを渡した場合のみ。
Ben Voigt

1
@MSalters:プログラムの正しい操作に副作用が必要な場合にのみ、静的初期化子を実行するパッチを作成できるとしたら、それはすばらしいことです。残念ながら、それを完全に行うにはしばしば停止の問題を解決する必要があると思うので、おそらくいくつかの追加のシンボルを含める側でエラーを起こす必要があるでしょう。これは基本的に、Iraが質問へのコメントで言っていることです。(ところで、「プログラムの正しい操作に必要ではない」とは、「未使用」の定義と、その用語が標準でどのように使用されているのかとは異なります)
Ben Voigt

2
Cの@BenVoigt、グローバル初期化には副作用があってはなりません(初期化子は定数式でなければなりません)
MM

2
@マット:しかし、それはC ++では真実ではありません...そして、それらは同じリンカーを共有します。
Ben Voigt 2014

25

gccとldのバージョンについては、ドキュメントを確認してください。

しかし、私(OS X gcc 4.0.1)の場合、ldでこれらを見つけます

-dead_strip

エントリポイントまたはエクスポートされたシンボルから到達できない関数とデータを削除します。

-dead_strip_dylibs

エントリポイントまたはエクスポートされたシンボルから到達できないdylibを削除します。つまり、リンク中にシンボルを提供しなかったdylibのロードコマンドコマンドの生成を抑制します。このオプションは、dylibに重要な初期化子があるなど、何らかの間接的な理由で実行時に必要なdylibに対してリンクする場合は使用しないでください。

そしてこの便利なオプション

-why_live symbol_name

symbol_nameへの一連の参照を記録します。にのみ適用され-dead_stripます。デッドストリップを削除する必要があると思われるものが削除されない理由をデバッグするのに役立ちます。

gcc / g ++ manには、コンパイル時に最適化が有効になっている場合にのみ、特定の種類のデッドコードの除去が実行されるという注記もあります。

これらのオプション/条件はコンパイラーには適用されない場合がありますが、ドキュメントで同様のものを探すことをお勧めします。


これはとは何の関係もないようmingwです。
Fabio 2017

-dead_stripgccオプションではありません。
ar2015

20

プログラミングの習慣も役に立ちます。たとえばstatic、特定のファイルの外部でアクセスされない関数に追加します。シンボルには短い名前を使用します(少し役立つ場合がありますが、多すぎません)。const char x[]可能な場合は使用してください。... このペーパーは、動的共有オブジェクトについて説明していますが、従った場合、最終的なバイナリ出力サイズを小さくするのに役立つ可能性のある提案を含めることができます(ターゲットがELFの場合)。


4
シンボルの短い名前を選択するとどのように役立ちますか?
fuz

1
シンボルが取り除かれていなければ、悲しいことですが、今は言う必要があるようです。
2016

@fuzこのペーパーは動的共有オブジェクト(.soLinuxなど)について話しているため、PythonのctypesFFIモジュールなどのAPI がそれらを使用して実行時に名前でシンボルを検索できるように、シンボル名を保持する必要があります。
ssokolow

18

答えは-fltoです。コンパイルとリンクの両方のステップに渡す必要があります。そうしないと、何も実行されません。

それは実際に非常にうまく機能します-私が書いたマイクロコントローラープログラムのサイズを以前のサイズの50%未満に縮小しました!

残念ながら、それは少しバグがあるように見えました-私は物事が正しく構築されていないインスタンスがありました。私が使用しているビルドシステム(QBS、これは非常に新しい)が原因である可能性がありますが、いずれにしても、可能であれば最終ビルドでのみ有効にして、そのビルドを完全にテストすることをお勧めします。


1
「-Wl、-gc-sections」はMinGW-W64では機能しません。「-flto」は機能します。ありがとう
rhbc73 2016年

出力アセンブリは非常に奇妙-fltoですが、それが舞台裏で何をしているのかわかりません。
ar2015

私は-fltoそれが各ファイルをアセンブリにコンパイルするのではなく、それらをLLVM IRにコンパイルし、最後のリンクがそれらをすべて1つのコンパイルユニットにあるかのようにコンパイルすると信じています。つまり、使用されていない関数やインラインでない関数static、そしておそらく他のものも排除できるということです。llvm.org/docs/LinkTimeOptimization.htmlを
Timmmm

13

厳密にはシンボルについてではありませんが、サイズについては- -Os-sフラグを使用してコンパイルしてください。-Os結果のコードを実行可能ファイルの最小サイズに最適化-sし、実行可能ファイルからシンボルテーブルと再配置情報を削除します。

ときどき-小さいサイズが必要な場合-異なる最適化フラグをいじることは、重要な場合とそうでない場合があります。たとえば、切り替え-ffast-mathたり、-fomit-frame-pointer場合によっては数十バイトも節約できます。


言語標準に準拠している限り、ほとんどの最適化の微調整で正しいコードが生成されますが、私は-ffast-math完全に標準に準拠したC ++コードに大混乱をもたらしたため、お勧めしません。
Raptor007 2017

11

Nemoの答えは正しいと思います。これらの手順が機能しない場合、問題は使用しているgcc / ldのバージョンに関連している可能性があります。演習として、ここで説明する手順を使用してサンプルプログラムをコンパイルしました

#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }

次に、次第に積極的なデッドコード削除スイッチを使用してコードをコンパイルしました。

gcc -Os test.c -o test.elf
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections -Wl,--strip-all

これらのコンパイルおよびリンクパラメータは、それぞれ8457、8164、および6160バイトの実行可能ファイルを生成しました。最も重要な貢献は、「strip-all」宣言によるものです。プラットフォームで同様の削減を行うことができない場合は、使用しているバージョンのgccがこの機能をサポートしていない可能性があります。Linux Mint 2.6.38-8-generic x86_64でgcc(4.5.2-8ubuntu4)、ld(2.21.0.20110327)を使用しています


8

strip --strip-unneeded実行可能ファイルのシンボルテーブルのみを操作します。実際には、実行可能コードは削除されません。

標準ライブラリは、それらのすべての機能を個別のオブジェクトファイルに分割し、それらをを使用して結合することにより、目的の結果を実現しますar。次に、結果のアーカイブをライブラリとしてリンクする(つまり-l your_library、ldにオプションを与える)と、ldには、実際に使用されるオブジェクトファイル、つまりシンボルのみが含まれます。

また、この同様の使用質問に対する回答の一部が見つかる場合もあります。


2
ライブラリ内の個別のオブジェクトファイルは、静的リンクを行う場合にのみ関連します。共有ライブラリでは、ライブラリ全体がロードされますが、実行可能ファイルには含まれません。
ジョナサンレフラー、2011

4

これが最近の機能であるため、これが現在の苦境に役立つかどうかはわかりませんが、グローバルな方法でシンボルの可視性を指定できます。-fvisibility=hidden -fvisibility-inlines-hiddenコンパイル時に渡すと、リンカーが後で不要なシンボルを取り除くのに役立ちます。(共有ライブラリとは対照的に)実行可能ファイルを作成する場合、これ以上行うことはありません。

詳細情報(およびライブラリなどの詳細なアプローチ)は、GCC wikiで入手できます


4

GCC 4.2.1マニュアルのセクション-fwhole-program

現在のコンパイル単位が、コンパイルされるプログラム全体を表すと想定します。を除くすべてのパブリック関数と変数、mainおよび属性によってマージされた変数はexternally_visible静的関数になり、プロシージャ間オプティマイザによってより積極的に最適化されます。このオプションはstatic単一ファイルで構成されるプログラムのキーワードの適切な使用と同等ですが、オプションと組み合わせて--combineこのフラグを使用すると、関数と変数が結合されたコンパイルユニット全体ではローカルになるため、ほとんどの小規模なCプログラムをコンパイルできます。単一のソースファイル自体。


うん、でもそれはおそらくどんなインクリメンタルコンパイルでも機能せず、おそらく少し遅くなるでしょう。
Timmmm 2014年

@Timmmm:あなたが考えているのではないかと思います-flto
Ben Voigt 2014

はい!その後、私はそれを見つけました(なぜそれが答えのどれでもないのですか?)。残念ながら、少しバグが多いように思われたので、最終ビルドのみに推奨し、そのビルドをたくさんテストします!
Timmmm 2014

-1

オブジェクトファイル(実行可能ファイルなど)でストリップバイナリを使用して、そこからすべてのシンボルをストリップできます。

注:ファイル自体を変更し、コピーを作成しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.