Skip to content

Commit 6e15e64

Browse files
committed
Restore stdcall-stripping for 32-bit import libs
This has gotten messy, as some .defs feature stdcall-mangled functions as well as C++ ones, and some hide stdcall-mangled functions via `DATA` suffix, so that we have to handle global variables with `@` characters etc. I switched from C to D, as `pragma(mangle)` is a big help when dealing with arbitrary identifiers.
1 parent 0af1c17 commit 6e15e64

File tree

1 file changed

+135
-13
lines changed

1 file changed

+135
-13
lines changed

windows/mingw/buildsdk.d

Lines changed: 135 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// source files extracted from MinGW-w64:
1111
// https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-v6.0.0.tar.bz2
1212
//
13-
// assumes VC tools cl, lib and ml installed and found through path
13+
// assumes dmd & VC tools cl, lib, link and ml installed and found through path
1414
// and configured to the appropriate architecture
1515
//
1616

@@ -87,23 +87,23 @@ void sanitizeDef(string defFile)
8787
{
8888
patchLines(defFile, defFile, (line)
8989
{
90+
if (line.length == 0 || line[0] == ';')
91+
return line;
92+
9093
if (line == "LIBRARY vcruntime140_app")
9194
return `LIBRARY "vcruntime140.dll"`;
9295

9396
// The MinGW-w64 .def files specify weak external symbols as 'alias == realName'.
94-
if (line.length > 1 && line[0] != ';')
97+
const i = line.indexOf("==");
98+
if (i > 0)
9599
{
96-
const i = line.indexOf(" == ");
97-
if (i > 0)
100+
const weakName = strip(line[0 .. i]);
101+
const realName = strip(line[i+2 .. $]);
102+
103+
if (weakName.indexOf(' ') < 0 && realName.indexOf(' ') < 0)
98104
{
99-
const weakName = line[0 .. i];
100-
const realName = line[i+4 .. $];
101-
102-
if (weakName.indexOf(' ') < 0 && realName.indexOf(' ') < 0)
103-
{
104-
weakSymbols[weakName] = realName;
105-
return ";" ~ line;
106-
}
105+
weakSymbols[weakName] = realName;
106+
return ";" ~ line;
107107
}
108108
}
109109

@@ -118,7 +118,11 @@ void sanitizeDef(string defFile)
118118

119119
// Don't export function 'atexit'; we have our own in msvc_atexit.c.
120120
if (line == "atexit")
121-
return "";
121+
return ";atexit";
122+
123+
// An apparent bug in lib32/shell32.def (there's 'ExtractIconW@12' too).
124+
if (line == "ExtractIconW@")
125+
return ";ExtractIconW@";
122126

123127
return line;
124128
});
@@ -156,6 +160,12 @@ void copyDefs(string inDir, string outDir)
156160

157161
void def2implib(string defFile)
158162
{
163+
if (!x64)
164+
{
165+
if (defWithStdcallMangling2implib(defFile))
166+
return;
167+
}
168+
159169
const libFile = setExtension(defFile, ".lib");
160170
const arch = x64 ? "X64" : "X86";
161171
runShell(`lib /MACHINE:` ~ arch ~ ` "/DEF:` ~ defFile ~ `" "/OUT:` ~ libFile ~ `"`);
@@ -182,6 +192,118 @@ string quote(string arg)
182192
return `"` ~ arg ~ `"`;
183193
}
184194

195+
/**
196+
* x86: the WinAPI symbol names in the .def files are stdcall-mangled
197+
* (trailing `@<N>`). These mangled names are required in the import
198+
* library, but the names of the DLL exports don't feature the stdcall
199+
* suffix.
200+
* `lib /DEF` doesn't support the required renaming functionality, so
201+
* we have to go through compiling a D file with the symbols and
202+
* building a DLL with renamed exports to get the appropriate import
203+
* library.
204+
*/
205+
bool defWithStdcallMangling2implib(string defFile)
206+
{
207+
import std.regex : ctRegex, matchFirst;
208+
209+
string[] functions;
210+
string[] fields;
211+
bool hasRenamedStdcall = false;
212+
213+
patchLines(defFile, defFile, (line)
214+
{
215+
if (line.length == 0 || line[0] == ';' ||
216+
line.startsWith("LIBRARY ") || line.startsWith("EXPORTS"))
217+
return line;
218+
219+
if (line.endsWith(" DATA"))
220+
{
221+
fields ~= line[0 .. $-5];
222+
return line;
223+
}
224+
225+
// include fastcall mangle (like stdcall, with additional leading '@')
226+
enum re = ctRegex!r"^@?([a-zA-Z0-9_]+)(@[0-9]+)";
227+
if (const m = matchFirst(line, re))
228+
{
229+
string lineSuffix = line[m[0].length .. $];
230+
if (lineSuffix.startsWith(m[2])) // e.g., 'JetAddColumnA@28@28'
231+
{
232+
/**
233+
* Actually not to be renamed, symbol is exported in mangled form.
234+
* Treat it like 'JetAddColumnA@28' though, because some libraries
235+
* export the same function as both 'JetAddColumnA' and 'JetAddColumnA@28',
236+
* and I don't know how to replicate that with our approach.
237+
*/
238+
lineSuffix = lineSuffix[m[2].length .. $];
239+
}
240+
241+
assert(!lineSuffix.startsWith("=")); // renamings not supported
242+
243+
hasRenamedStdcall = true;
244+
functions ~= m[1];
245+
// keep the line suffix (e.g., ' @100' => ordinal 100)
246+
return m[0] ~ "=" ~ m[1] ~ lineSuffix;
247+
}
248+
249+
const firstSpaceIndex = line.indexOf(' ');
250+
const strippedLine = firstSpaceIndex < 0 ? line : line[0 .. firstSpaceIndex];
251+
const equalsIndex = strippedLine.indexOf('=');
252+
const functionName = equalsIndex > 0 ? strippedLine[equalsIndex+1 .. $] : strippedLine;
253+
functions ~= functionName;
254+
return line;
255+
});
256+
257+
if (!hasRenamedStdcall)
258+
return false;
259+
260+
string src = "module dummy;\n";
261+
alias Emitter = string delegate();
262+
void emitOnce(ref bool[string] emittedSymbols, string symbolName, Emitter emitter)
263+
{
264+
if (symbolName !in emittedSymbols)
265+
{
266+
src ~= emitter() ~ "\n";
267+
emittedSymbols[symbolName] = true;
268+
}
269+
}
270+
271+
bool[string] emittedFunctions;
272+
foreach (i, name; functions)
273+
{
274+
emitOnce(emittedFunctions, name, ()
275+
{
276+
const linkage = name[0] == '?' ? "C++" : "C";
277+
return `pragma(mangle, "%s") extern(%s) void func%d() {}`.format(name, linkage, i);
278+
});
279+
}
280+
281+
bool[string] emittedFields;
282+
foreach (i, name; fields)
283+
{
284+
emitOnce(emittedFields, name, ()
285+
{
286+
const linkage = name[0] == '_' ? "C" : "C++";
287+
return `pragma(mangle, "%s") extern(%s) __gshared int field%d;`.format(name, linkage, i);
288+
});
289+
}
290+
291+
const dFile = setExtension(defFile, ".d");
292+
const objFile = setExtension(defFile, ".obj");
293+
const dllFile = setExtension(defFile, ".dll");
294+
295+
std.file.write(dFile, src);
296+
runShell(`dmd -c -betterC -m32mscoff "-of=` ~ objFile ~ `" ` ~ quote(dFile));
297+
runShell("link /NOD /NOENTRY /DLL " ~ quote(objFile) ~ ` "/OUT:` ~ dllFile ~ `" "/DEF:` ~ defFile ~ `"`);
298+
299+
std.file.remove(dFile);
300+
std.file.remove(objFile);
301+
std.file.remove(dllFile);
302+
std.file.remove(setExtension(dllFile, ".exp"));
303+
304+
return true;
305+
}
306+
185307
void c2lib(string outDir, string cFile)
186308
{
187309
const obj = buildPath(outDir, baseName(cFile).setExtension(".obj"));

0 commit comments

Comments
 (0)