Microsoft Visual C/C++ のインポートライブラリからシンボル検索

いつものように(?)「アタシが嬉しい」だけのもの。

Microsoft Visual C++ Compiler for Python 2.7の場合、msdev が添付じゃないので、どうにかこうにか Makefile とか setup.py とか SCons とか, etc. をでっち上げる必要があるのだけれど、「msdev経由の場合」に「暗黙で」追加されるインポートライブラリがあって。msproj とかに明示的に書かれていなくてもリンク出来てしまうのね。けどMicrosoft Visual C++ Compiler for Python 2.7だけだとそうはならない。

と、思う。少なくとも msproj には書かれていないものをリンクする必要がある場合が多いのは確か、なんだけれど、ひょっとしたら pragma の制御とかも関係してるかも。

ともあれ、リンクエラーになった場合に、何をリンクしなければならないのかを調べる必要があるわけです。それって、Microsoft は全部丁寧にドキュメントしてくれているので、すぐに調べられることは調べられるんだけれど、昔から Microsoft のドキュメントサイトは「重」くてあまり気がすすまなかったりもするわけね。なんとかローカルで調べたい、と。

発想は単純ですよ。画像だけで通じるかな?
dumpbin_libs

で、この情報を使って検索する Python スクリプトに仕立て上げた、っと:

 1 #! /bin/env python
 2 # -*- coding: utf-8 -*-
 3 #
 4 # filename: find_msvc_implib.py
 5 #
 6 import sys
 7 import os
 8 import re
 9 import pickle
10 import hashlib
11 from os import environ
12 from os import path
13 from glob import glob
14 from subprocess import Popen, PIPE
15 from tempfile import gettempdir
16 
17 _cache_dir = path.join(gettempdir(), "." + __file__)
18 if not path.exists(_cache_dir):
19     os.mkdir(_cache_dir)
20 
21 _ENC = sys.stdout.encoding or 'cp932'
22 _LIBS = map(lambda p: p.decode(_ENC).replace(os.sep, '/'),
23            filter(lambda s: s.strip(),
24                   re.sub(r";\s*$", "", environ['LIB']).split(';')))
25 
26 _digester = hashlib.md5
27 
28 def find(patterns, is_regex, basename):
29     if is_regex:
30         rgxes = [re.compile(pat) for pat in patterns]
31         matcher = lambda sym: any([r.match(sym) for r in rgxes])
32     else:
33         matcher = lambda sym: any([pat in sym for pat in patterns])
34 
35     for lib in _LIBS:
36         for fn in glob(path.join(lib, "*.[Ll][Ii][Bb]")):
37             cache_file = path.join(
38                 _cache_dir, u"%s.pickle" % 
39                     _digester(path.normpath(fn)).hexdigest())
40             if path.exists(cache_file):
41                 cur = pickle.load(open(cache_file, "rb"))
42             else:
43                 proc = Popen(['dumpbin', '/HEADERS', fn], stdout=PIPE)
44                 stdo, stde = proc.communicate()
45                 cur = []
46                 for line in re.split("\r?\n", stdo):
47                     m1 = re.match('^ +Name +: +(.*)', line)
48                     m2 = re.match('^ +COMDAT; sym= (.*)', line)
49                     if m1:
50                         cur.append(m1.group(1))
51                     elif m2:
52                         cur.append(m2.group(1))
53                 if cur:
54                     pickle.dump(cur, open(cache_file, "wb"))
55             for sym in cur:
56                 if matcher(sym):
57                     if not basename:
58                         print(sym, fn.replace(os.sep, "/"))
59                     else:
60                         print(sym, path.basename(fn))
61 
62 if __name__ == '__main__':
63     import argparse
64     parser = argparse.ArgumentParser(
65         description='search library which has specified symbol.')
66     parser.add_argument('-r', '--regex',
67                         action='store_true', default=False,
68                         help='use pattern as regular expression')
69     parser.add_argument('-b', '--basename',
70                         action='store_true', default=False,
71                         help='report library name as basename rather than fullpath.')
72     parser.add_argument('patterns', nargs='+',
73                         help='symbol (or substring of it) that you want to search')
74     args = parser.parse_args(sys.argv[1:])
75     find(args.patterns, args.regex, args.basename)

「Visual C++ 2008 64-bit Command Prompt」(等)の環境から動かしてくださいな。でないと動作しません。

一回目だけは dumpbin.exe を本当に呼び出すので時間かかりますが、二回目以降は pickle から復元するので少し速いです。(/tmp に pickle 置き場作ってるのはイケてないですけど、ま、直したければご自分でどーぞ。)

こんな感じです:

 1 me@host: ~$ find_msvc_implib.py -h
 2 usage: find_msvc_implib.py [-h] [-r] [-b] patterns [patterns ...]
 3 
 4 search library which has specified symbol.
 5 
 6 positional arguments:
 7   patterns        symbol (or substring of it) that you want to search
 8 
 9 optional arguments:
10   -h, --help      show this help message and exit
11   -r, --regex     use pattern as regular expression
12   -b, --basename  report library name as basename rather than fullpath.
 1 me@host: ~$ ./find_msvc_implib.py MessageBox
 2 ('__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 3 ('$pdata$__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 4 ('$unwind$__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 5 ('"`string\'" (??_C@_0M@DLDCCGNP@MessageBoxW?$AA@)', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 6 ('__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 7 ('$pdata$__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 8 ('$unwind$__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
 9 ('"`string\'" (??_C@_0M@CHKKJDAI@MessageBoxA?$AA@)', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmt.lib')
10 ('__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
11 ('$pdata$__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
12 ('$unwind$__crtMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
13 ('"`string\'" (??_C@_0M@DLDCCGNP@MessageBoxW?$AA@)', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
14 ('__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
15 ('$pdata$__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
16 ('$unwind$__crtMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
17 ('"`string\'" (??_C@_0M@CHKKJDAI@MessageBoxA?$AA@)', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/Lib/amd64/libcmtd.lib')
18 ('SoftwareUpdateMessageBox', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/shdocvw.lib')
19 ('ShellMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/ShLwApi.Lib')
20 ('MessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
21 ('MessageBoxExA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
22 ('MessageBoxExW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
23 ('MessageBoxIndirectA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
24 ('MessageBoxIndirectW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
25 ('MessageBoxTimeoutA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
26 ('MessageBoxTimeoutW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
27 ('MessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
28 ('PrinterMessageBoxA', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/WinSpool.Lib')
29 ('PrinterMessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/WinSpool.Lib')
1 me@host: ~$ ./find_msvc_implib.py '^MessageBox.*W' -r
2 ('MessageBoxExW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
3 ('MessageBoxIndirectW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
4 ('MessageBoxTimeoutW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
5 ('MessageBoxW', u'C:/Users/hhsprings/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/WinSDK/Lib/x64/User32.Lib')
1 me@host: ~$ ./find_msvc_implib.py '^MessageBox.*W' -r -b
2 ('MessageBoxExW', u'User32.Lib')
3 ('MessageBoxIndirectW', u'User32.Lib')
4 ('MessageBoxTimeoutW', u'User32.Lib')
5 ('MessageBoxW', u'User32.Lib')

色々改良の余地はあると思うけど、「アタシのため」であればそれで良い。ぉ。