forked from microsoft/vcrt-forwarders
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGenForwarders.py
More file actions
142 lines (113 loc) · 4.66 KB
/
GenForwarders.py
File metadata and controls
142 lines (113 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import glob
import os
import subprocess
import sys
if len(sys.argv) != 3:
print("Usage: GenForwarders.py <vs_folder> <vc_version_to_generate>")
print("Last version generated 14.21.27702")
exit()
VSFolder = sys.argv[1]
VCVersion = sys.argv[2]
archs = ["x86", "x64", "arm64"]
importLibMapping = {
"concrt140" : "concrt.lib",
"concrt140d" : "concrtd.lib",
"msvcp140_1" : "msvcprt.lib",
"msvcp140_1d" : "msvcprt.lib",
"msvcp140_2" : "msvcprt.lib",
"msvcp140_2d" : "msvcprt.lib",
"msvcp140" : "msvcprt.lib",
"msvcp140d" : "msvcprtd.lib",
"vcamp140" : "vcamp.lib",
"vcamp140d" : "vcampd.lib",
"vccorlib140" : "vccorlib.lib",
"vccorlib140d" : "vccorlibd.lib",
"vcomp140" : "vcomp.lib",
"vcomp140d" : "vcompd.lib",
"vcruntime140" : "vcruntime.lib",
"vcruntime140d" : "vcruntimed.lib",
"vcruntime140_1" : "vcruntime.lib",
"vcruntime140_1d" : "vcruntimed.lib",
};
def GetModules(arch):
modules = []
releaseFolder = os.path.join(VSFolder, r"VC\Redist\MSVC", VCVersion, arch)
releaseModules = glob.glob(os.path.join(releaseFolder, r"**\*.dll"), recursive=True)
modules += [[module, True] for module in releaseModules]
debugFolder = os.path.join(VSFolder, r"VC\Redist\MSVC", VCVersion, r"debug_nonredist", arch)
debugModules = glob.glob(os.path.join(debugFolder, r"**\*.dll"), recursive=True)
modules += [[module, False] for module in debugModules]
return modules
def GetImportLibFolder(arch):
return os.path.join(VSFolder, r"VC\Tools\MSVC", VCVersion, "lib", arch)
def GetDumpbin():
return os.path.join(VSFolder, r"VC\Tools\MSVC", VCVersion, r"bin\Hostx86\x86\dumpbin.exe")
def GenerateSymbolMapping(importLibPath):
symbolTable = {}
output = subprocess.Popen([GetDumpbin(), "/headers", importLibPath], stdout=subprocess.PIPE)
for line in iter(output.stdout.readline,b''):
strippedLine = line.strip().decode("windows-1251")
# ignore all lines other than for symbol entry
if "Symbol name" not in strippedLine:
continue
# skip a few lines to get to the nameType and name field
output.stdout.readline() # type
nameType = output.stdout.readline().strip().decode("windows-1251")
output.stdout.readline() # hint
name = output.stdout.readline().strip().decode("windows-1251")
# symbol line is in format of Symbol name : <symbol> [(extra info)]
symbol = strippedLine.split(":")[1].strip().split(" ")[0].strip()
name = name.split(":")[1].strip()
nameType = nameType.split(":")[1].strip()
if name in symbolTable:
raise ValueError
if nameType == "no prefix":
symbolTable[name] = symbol
elif nameType == "name":
symbolTable[name] = name
elif nameType == "undecorate":
symbolTable[name] = "_" + name
else:
raise ValueError(nameType)
return symbolTable
# Generate the same folder structure as the repo
# i.e. 140_debug\vcrt_fwd_x64_debug\concrt140d_app
def GetOutputFolder(arch, isRelease, module):
folderSuffix = "release" if isRelease else "debug"
return str.format(r"140_{0}\vcrt_fwd_{1}_{0}\{2}_app", folderSuffix, arch, module)
# main logic to go through each module and generate forwarders
errors = []
for arch in archs:
for item in GetModules(arch):
modulePath = item[0]
module = os.path.splitext(os.path.basename(modulePath))[0]
symbolMapping = GenerateSymbolMapping(os.path.join(GetImportLibFolder(arch), importLibMapping[module]))
outputFolder = GetOutputFolder(arch, item[1], module)
if not os.path.exists(outputFolder):
os.makedirs(outputFolder)
outputFile = open(os.path.join(outputFolder, module + "_app.cpp"), "w")
output = subprocess.Popen([GetDumpbin(), "/exports", modulePath], stdout=subprocess.PIPE)
headerParsed = False
for line in iter(output.stdout.readline,b''):
strippedLine = line.strip().decode("windows-1251")
if not headerParsed:
if ("ordinal" in strippedLine and "hint" in strippedLine):
# Skip line after header
output.stdout.readline()
headerParsed = True
continue
if strippedLine == "Summary":
break
if strippedLine == "":
continue
function = list(filter(None, strippedLine.split(" ")))[3]
if function not in symbolMapping:
# handle edge case for _EH_PROLOG as for some reason it is not in the symbol table and is only in X86.
if function == "_EH_prolog":
outputFile.write(str.format("""#pragma comment(linker, "/export:{0}={1}.{2}")\n""", "__EH_prolog", module, function))
else:
errors += [[module, function]]
else:
outputFile.write(str.format("""#pragma comment(linker, "/export:{0}={1}.{2}")\n""", symbolMapping[function], module, function))
outputFile.close()
print(errors)