静的ライブラリのすべてのシンボルを強制的にリンク(使用されていないものを含む)

7379 ワード

C++プログラムは、静的ライブラリをリンクするときに、静的ライブラリのいくつかのメソッドがどこにも呼び出されていない場合、最終的に呼び出されていないメソッドや変数は破棄され、ターゲットプログラムにリンクされません.これにより、バイナリファイルを生成するボリュームが大幅に減少します.しかし、静的ライブラリのいくつかの方法がどこにも使用されていない場合でも、使用されていないコードを最終的なバイナリファイルにコンパイルしたい場合があります.
どうしてこのような需要があるのですか?確かに、このような需要があるのは少数の状況ですが、次のような需要に遭遇すると、必要になります.例:
  • ダイナミックプラグインメカニズム.コードにはメソッドが直接呼び出されませんが、実行時にメソッドを動的にロードして実行したい場合があります.
  • は、コードオーバーライド率統計を実行する.静的ライブラリのすべてのコードのオーバーライド状況を統計する必要があります.使用されるコードのオーバーライド状況だけではありません.

  • gccコンパイルの場合、比較的やりやすいので、--whole-archiveリンクオプションを追加するだけです.しかし、Windowsプラットフォームでは、マイクロソフトのコンパイラにはこのようなオプションはありません.最も近いオプションは/OPT:NOREFです.
    ドキュメントを参照:https://msdn.microsoft.com/en-us/library/bxwfs976.aspx説明:/OPT:REF eliminates functions and data that are never referenced;/OPT:NOREF keeps functions and data that are never referenced.
    /OPT:NOREFはDebugでデフォルトで開かれており、このプロジェクトで使用されていない関数と変数のみを強制的に保持できます.参照される静的ライブラリの使用されていない関数と変数は有効ではありません.マイクロソフトのバグだと思っている人もいますが、この投稿ではLINK.EXE BUG:/OPT:NOREF option doesn't work!
    同じ問題に遭遇したのは私だけではありません.例えばStackOverFlowでは、What is the Visual studio equivalent to GNU ld option--whole-archiveという質問があります.
    使用されていないシンボルへのリンクを強制するために/INCLUEオプションを使用することをお勧めする人もいれば、/OPT:NOREF(明らかにだめ)を使用する人もいます.
    シンボルの強制リンクを指定するには、/INLUDEを使用します.しかし、静的ライブラリに何百ものシンボルが強制/INCLUDEを必要としている場合は、どうすればいいですか?
    したがって、最良の方法は、上記の/OPT:NOREF BUGの投稿で誰かが言及した方法であり、コードで使用することです.
    #pragma comment(linker, "/include:?emptyreference@Noisy@@QAEXXZ")

    上記の方法では、リンクにincludeのシンボルを強制することができます.include:シンボル名の後ろにあります.include静的ライブラリ内のすべてのシンボルを強制する場合は、静的ライブラリ内のすべてのシンボルを探し出し、上記の方法でincludeを強制する必要があります.
    人が手ですべてのSymbolsを見つけて、上のコードを追加するのはあまり頼りになりません.Symbolsのフォーマットの読み取りが悪すぎてメンテナンスができない一方で、静的ライブラリシンボル情報が変更された場合、このメンテナンスのコストはさらに大きくなります.だから、このプロセスを自動的に完了させなければなりません.
    静的ライブラリのすべてのシンボルリストを表示します.Linuxではnmを使用できます.Windowsプラットフォームではdumpbinを使用できます.
    dumbinを実行します.exeは、Visual Studioの開発コマンドライン環境で実行する必要があることに注意してください.しかし、Developer Command Promptで実行する必要がない小さなテクニックがあります.つまり、VS 2013環境であれば、バッチを作成し、先頭に追加します.
    @echo off
    if defined VS120COMNTOOLS (
        call "%VS120COMNTOOLS%\vsvars32.bat")

    dumpbin/LinKERMEMBER xxxを使用しています.Libは、静的ライブラリMyLibを表示するなど、すべての記号の名前をリストすることができます.libすべての記号:
    d:\Code\Cpp\LinkAllSymbols\Debug>dumpbin.exe /linkermember:1 MyLib.lib
    Microsoft (R) COFF/PE Dumper Version 12.00.30501.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
     
     
    Dump of file MyLib.lib
     
    File Type: LIBRARY
     
    Archive member name at 8: /
    557D4C17 time/date Sun Jun 14 17:40:39 2015
             uid
             gid
           0 mode
          ED size
    correct header end
     
        9 public symbols
     
          328 ??4Turtle@@QAEAAV0@ABV0@@Z
          328 ??_C@_0M@KEAKLOKJ@Turtle?5run?4?$AA@
          328 ?Download@@YAHXZ
          328 ?Run@Turtle@@QAEXXZ
         19CE ?FishRun@@YAXXZ
         19CE ?Run@Fish@@QAEXXZ
         2D16 ??_C@_08EMEDHABH@Dog?5run?4?$AA@
         2D16 ?Foo@@YAHXZ
         2D16 ?Run@Dog@@QAEXXZ
     
      Summary
     
            28B4 .debug$S
              F0 .debug$T
             102 .drectve
              15 .rdata
               C .rtc$IMZ
               C .rtc$TMZ
             15A .text$mn

    したがってdumpbinを実行し、出力結果からすべてのシンボル名を抽出し、#pragma comment(linker,"/include:xxx")コードを自動的に生成するだけでよい.
    そこで、Pythonスクリプトを書いてdumpbinを実行し、正規表現ですべての記号名を取得し、すべての記号を強制includeしたヘッダファイルを自動的に生成しました.キーコードは次のとおりです.
    import re
     
    regex = re.compile(r"\s+.*\s([\?_]+.*)")
     
    exclude = []
     
    def gen_header_file_for_lib(lib_path, header_path):
        cmd = ['dumpbin.exe','/linkermember:1', lib_path]
        lines = execute_command(cmd)
        symbols = find_matches(lines, regex, exclude)
     
        with open(header_path, 'w') as f:
            header_guard = "LINK_ALL_SYMBOLS_H_"
            f.write("#ifndef " + header_guard + '
    ') f.write("#define " + header_guard + '
    ') f.write("// Generated by GenLinkerSymbols.py. Do not modify!

    ") for symbol in symbols: pragma_line = '#pragma comment(linker, "/include:' + symbol + '")' f.write(pragma_line + '
    ') f.write("
    #endif // " + header_guard + '
    ') print("Link symbols count: %s" % len(symbols)) def find_matches(lines, regex, exclude_list): def match(line): m = regex.match(line) if m: return m.group(1).split()[0] return None def exclude_filter(line): if not line: return False for exclude in exclude_list: if line.find(exclude) >= 0: return False return True matches = filter(exclude_filter, map(match, lines)) return list(set(matches))

    Visual Studioエンジニアリング構成のPost-Build Eventと組み合わせると、静的ライブラリをコンパイルした後にヘッダファイルを自動的に更新できます.例:
    python ..\GenSymbolsHeader.py $(OutDir)$(TargetName)$(TargetExt) ..\Include\LinkAllSymbols.h

    このスタティックライブラリを使用するエンジニアリングコードでは、#include"LinkAllSymbols.h"だけで済みます.
    コントラスト
    OpenCppCoverageを使用してコードオーバーライド率テストを行い、以下の比較を行います.
    通常、linker時にinclude静的ライブラリのすべてのシンボルを強制しない場合、コードオーバーライド率の結果は次のとおりです.
    上記の方法でLinkAllSymbolsを自動的に生成する.h並#include“LinkAllSymbols.h”、カバー率結果:
    github
    すべてのコードは次のとおりです.https://github.com/coderzh/LinkAllSymbols