ライブラリ*.libから関数名を抽出する
言語と開発環境に戻る。
概要
ライブラリを生成したときに所望の関数がちゃんと取り込まれているか確認するときに*.libファイルの中に関数名が存在しているかを抽出して確かめる手順について記述します。
大規模なプロジェクトでは関数名が多すぎて、使いたいとおもっている関数が確実に出力されているのかを確かめるにはやはり、生成sareta*.libファイルに関数名を存在するかチェックした方が早い場合があります。
dumpbinコマンドで関数名を得ることはできますが、膨大な関数名から全体を見渡すのは大変です。引数が違うだけで、同じ名前の関数がいっぱいあったり、名前マングリングによって余計な情報が取り込まれていたりします。すっきり関数名だけを抽出してやろうというのがこの記事の目的です。VisualStudioのツールから起動できるPowerShellスクリプトでdumpbinの結果から関数名の行だけをテキストに出力する処理をしてからPythonで名前マングリングを解除した関数名を取得しつつ、同じ関数名を一つに集約するという処理を行います。
まず以下のようなdumpbinコマンドをPowerShellスクリプトによって一括で処理しながら、関数名に関する情報だけを取得します。この時点では名前マングリングがなされていて、読みにくいですが、まずはやってみましょう。
Visual Studio のツールのPowerShellを選択してPowershellを起動します。そして、ライブラリファイルのあるフォルダをカレントディレクトにして、以下のコマンドをペロっと貼ります。libboost_regex-vc143-mt-gd-x64-1_89-non_icu.libというファイル名は以下の例の場合のライブラリファイル名ですので、自分で調べたいライブラリファイル名に変更して動作させてみて下さい。
PS C:\(libファイルのあるフォルダへのパス)\lib> dumpbin /SYMBOLS /NOLOGO libboost_regex-vc143-mt-gd-x64-1_89-non_icu.lib |
Select-String "External" |
ForEach-Object {
if ($_ -match "\|\s*([^\s]+)") { $matches[1] }
} | Sort-Object | Get-Unique > short_symbol_names.txt
例えば、上記の例では自分で作ったboost.regexのデバッグ版64ビットアドレス方式のlibファイルの中身を見ようとしていて、short_symbol_names.txtというファイル名に同じディレクトリへ出力します。
つぎにVisual StudioでPython プロジェクトを作ります。PythonはVisual Studioで動かさなくてもいいので、お好きな環境でペロっとして下さい。
import subprocess
import re
# 入力ファイル:デコレート名のリスト
input_file = r"C:\Users\(UserID)\source\repos\boost_1_89_0\stage\lib\short_symbol_names.txt"
# 出力ファイル:関数名のみ
output_file = r"C:\Users\(UserID)\source\repos\boost_1_89_0\stage\lib\demangled.txt"
# undname.exe のフルパスを指定
undname_path = r"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.43.34808\bin\Hostx64\x64\undname.exe"
# 2022と14.43.34808は自分のVisualSutdioの環境の存在するフォルダ名のバージョン番号に合わせる
# 関数名の重複を避けるため set で保持
function_names = set()
# 入力ファイルを読み込む
with open(input_file, "r", encoding="utf-16") as f:
symbols = [line.strip() for line in f]
for sym in symbols:
try:
# undname を実行
result = subprocess.run([undname_path, sym],
capture_output=True, text=True, check=True)
demangled = result.stdout.strip()
# "is :-" の行だけを抽出
for line in demangled.splitlines():
if "is :-" in line:
line = line.split("is :-")[-1].strip()
# 関数名だけ抽出する正規表現
# クラス名::関数名、デストラクタ、演算子など対応
m = re.search(r'(?:[\w:]+::)?(~?operator[^\s(]*|[\w~]+)\s*\(', line)
if m:
func_name = m.group(1)
function_names.add(func_name)
break # "is :-" 行は1行だけ処理
except subprocess.CalledProcessError:
# undname が失敗した場合はスキップ
print(f"undname failed for: {sym}")
except FileNotFoundError:
print(f"undname.exe not found: {undname_path}")
break
# 関数名をソートして出力
with open(output_file, "w", encoding="utf-8") as out:
for name in sorted(function_names):
out.write(name + "\n")
print(f"Extraction completed: {len(function_names)} unique function names")
プログラム中の入力ファイル名と出力ファイル名およびundname.exeコマンド実行ファイルのあるパスの指定は自身の環境に合わせて指定してください。管理人はBoostのライブラリを調査するのに使ったときの例として記述しています。
これで関数名を抽出したファイルが取得できます。やってみて。適当に作ったものだから、なんか抜けがある可能性がありますので、そこはご了承下さい。自分でも精査していかんとな。抜けがないかの調査はできていません。抜けがあるようでしたらご自分で改良してみて下さい。
言語と開発環境に戻る。