1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <stdarg.h>
6
7 #ifdef _MSC_VER
8 #define strcasecmp(_String1, _String2) _stricmp(_String1, _String2)
9 #define strncasecmp(_String1, _String2, _MaxCount) _strnicmp(_String1, _String2, _MaxCount)
10 #endif
11
12 #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
13
14 typedef struct _STRING
15 {
16 const char *buf;
17 int len;
18 } STRING, *PSTRING;
19
20 typedef struct
21 {
22 STRING strName;
23 STRING strTarget;
24 int nCallingConvention;
25 int nOrdinal;
26 int nStackBytes;
27 int nArgCount;
28 int anArgs[30];
29 unsigned int uFlags;
30 int nNumber;
31 unsigned nStartVersion;
32 unsigned nEndVersion;
33 int bVersionIncluded;
34 } EXPORT;
35
36 #if 0 // Debug helper function
37 void
38 PrintExport(EXPORT *pexp)
39 {
40 fprintf(stderr, "strName='%.*s'\n", pexp->strName.len, pexp->strName.buf);
41 fprintf(stderr, "strName='%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
42 fprintf(stderr, "nCallingConvention=%u\n", pexp->nCallingConvention);
43 fprintf(stderr, "nOrdinal=%u\n", pexp->nOrdinal);
44 fprintf(stderr, "nStackBytes=%u\n", pexp->nStackBytes);
45 fprintf(stderr, "nArgCount=%u\n", pexp->nArgCount);
46 fprintf(stderr, "uFlags=0x%x\n", pexp->uFlags);
47 fprintf(stderr, "nNumber=%u\n", pexp->nNumber);
48 fprintf(stderr, "nStartVersion=%u\n", pexp->nStartVersion);
49 fprintf(stderr, "nEndVersion=%u\n", pexp->nEndVersion);
50 fprintf(stderr, "bVersionIncluded=%u\n", pexp->bVersionIncluded);
51 }
52 #endif
53
54 enum _ARCH
55 {
56 ARCH_X86,
57 ARCH_AMD64,
58 ARCH_IA64,
59 ARCH_ARM,
60 ARCH_ARM64,
61 ARCH_PPC
62 };
63
64 typedef int (*PFNOUTLINE)(FILE *, EXPORT *);
65 int gbMSComp = 0;
66 int gbImportLib = 0;
67 int gbNotPrivateNoWarn = 0;
68 int gbTracing = 0;
69 int giArch = ARCH_X86;
70 int gbDbgExports = 0;
71 char *pszArchString = "i386";
72 char *pszArchString2;
73 char *pszSourceFileName = NULL;
74 char *pszDllName = NULL;
75 char *gpszUnderscore = "";
76 int gbDebug;
77 unsigned guOsVersion = 0x502;
78 #define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__))
79
80 enum
81 {
82 FL_PRIVATE = 1,
83 FL_STUB = 2,
84 FL_NONAME = 4,
85 FL_ORDINAL = 8,
86 FL_NORELAY = 16,
87 FL_RET64 = 32,
88 FL_REGISTER = 64,
89 FL_IMPSYM = 128,
90 };
91
92 enum
93 {
94 CC_STDCALL,
95 CC_CDECL,
96 CC_FASTCALL,
97 CC_THISCALL,
98 CC_EXTERN,
99 CC_STUB,
100 };
101
102 enum
103 {
104 ARG_LONG,
105 ARG_PTR,
106 ARG_STR,
107 ARG_WSTR,
108 ARG_DBL,
109 ARG_INT64,
110 ARG_INT128,
111 ARG_FLOAT
112 };
113
114 const char* astrCallingConventions[] =
115 {
116 "STDCALL",
117 "CDECL",
118 "FASTCALL",
119 "THISCALL",
120 "EXTERN"
121 };
122
123 /*
124 * List of OLE exports that should be PRIVATE and not be assigned an ordinal.
125 * In case these conditions are not met when linking with MS LINK.EXE, warnings
126 * LNK4104 and LNK4222 respectively are emitted.
127 */
128 static const char* astrOlePrivateExports[] =
129 {
130 "DllCanUnloadNow",
131 "DllGetClassObject",
132 "DllGetClassFactoryFromClassString",
133 "DllGetDocumentation",
134 "DllInitialize",
135 "DllInstall",
136 "DllRegisterServer",
137 "DllRegisterServerEx",
138 "DllRegisterServerExW",
139 "DllUnload",
140 "DllUnregisterServer",
141 "RasCustomDeleteEntryNotify",
142 "RasCustomDial",
143 "RasCustomDialDlg",
144 "RasCustomEntryDlg",
145 };
146
147 static
148 int
IsSeparator(char chr)149 IsSeparator(char chr)
150 {
151 return ((chr <= ',' && chr != '$' && chr != '#') ||
152 (chr >= ':' && chr < '?') );
153 }
154
155 int
CompareToken(const char * token,const char * comparand)156 CompareToken(const char *token, const char *comparand)
157 {
158 while (*comparand)
159 {
160 if (*token != *comparand) return 0;
161 token++;
162 comparand++;
163 }
164 if (IsSeparator(comparand[-1])) return 1;
165 if (!IsSeparator(*token)) return 0;
166 return 1;
167 }
168
169 const char *
ScanToken(const char * token,char chr)170 ScanToken(const char *token, char chr)
171 {
172 while (!IsSeparator(*token))
173 {
174 if (*token == chr) return token;
175 token++;
176 }
177 return 0;
178 }
179
180 const char *
NextLine(const char * pc)181 NextLine(const char *pc)
182 {
183 while (*pc != 0)
184 {
185 if (pc[0] == '\n' && pc[1] == '\r') return pc + 2;
186 else if (pc[0] == '\n') return pc + 1;
187 pc++;
188 }
189 return pc;
190 }
191
192 int
TokenLength(const char * pc)193 TokenLength(const char *pc)
194 {
195 int length = 0;
196
197 while (!IsSeparator(*pc++)) length++;
198
199 return length;
200 }
201
202 const char *
NextToken(const char * pc)203 NextToken(const char *pc)
204 {
205 /* Skip token */
206 while (!IsSeparator(*pc)) pc++;
207
208 /* Skip white spaces */
209 while (*pc == ' ' || *pc == '\t') pc++;
210
211 /* Check for end of line */
212 if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0;
213
214 /* Check for comment */
215 if (*pc == '#' || *pc == ';') return 0;
216
217 return pc;
218 }
219
220 void
OutputHeader_stub(FILE * file)221 OutputHeader_stub(FILE *file)
222 {
223 fprintf(file, "/* This file is autogenerated, do not edit. */\n\n"
224 "#include <stubs.h>\n");
225
226 if (gbTracing)
227 {
228 fprintf(file, "#include <wine/debug.h>\n");
229 fprintf(file, "#include <inttypes.h>\n");
230 fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n");
231 }
232
233 fprintf(file, "\n");
234 }
235
236 int
OutputLine_stub(FILE * file,EXPORT * pexp)237 OutputLine_stub(FILE *file, EXPORT *pexp)
238 {
239 int i;
240 int bRelay = 0;
241 int bInPrototype = 0;
242
243 /* Workaround for forwarded externs. See here for an explanation:
244 * https://stackoverflow.com/questions/4060143/forwarding-data-in-a-dll */
245 if (gbMSComp &&
246 (pexp->nCallingConvention == CC_EXTERN) &&
247 (pexp->strTarget.buf != NULL) &&
248 (!!ScanToken(pexp->strTarget.buf, '.')))
249 {
250 fprintf(file, "#pragma comment(linker,\"/export:%s%.*s=%.*s,DATA\")\n\n",
251 gpszUnderscore, pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
252 return 0;
253 }
254
255 if (pexp->nCallingConvention != CC_STUB &&
256 (pexp->uFlags & FL_STUB) == 0)
257 {
258 /* Only relay trace stdcall C functions */
259 if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL)
260 || (pexp->uFlags & FL_NORELAY)
261 || (pexp->strName.buf[0] == '?'))
262 {
263 return 0;
264 }
265 bRelay = 1;
266 }
267
268 /* Declare the "real" function */
269 if (bRelay)
270 {
271 fprintf(file, "extern ");
272 bInPrototype = 1;
273 }
274
275 do
276 {
277 if (pexp->uFlags & FL_REGISTER)
278 {
279 /* FIXME: Not sure this is right */
280 fprintf(file, "void ");
281 }
282 else if (pexp->uFlags & FL_RET64)
283 {
284 fprintf(file, "__int64 ");
285 }
286 else
287 {
288 fprintf(file, "int ");
289 }
290
291 if ((giArch == ARCH_X86) &&
292 pexp->nCallingConvention == CC_STDCALL)
293 {
294 fprintf(file, "__stdcall ");
295 }
296
297 /* Check for C++ */
298 if (pexp->strName.buf[0] == '?')
299 {
300 fprintf(file, "stub_function%d(", pexp->nNumber);
301 }
302 else
303 {
304 if (!bRelay || bInPrototype)
305 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
306 else
307 fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf);
308 }
309
310 for (i = 0; i < pexp->nArgCount; i++)
311 {
312 if (i != 0) fprintf(file, ", ");
313 switch (pexp->anArgs[i])
314 {
315 case ARG_LONG: fprintf(file, "long"); break;
316 case ARG_PTR: fprintf(file, "void*"); break;
317 case ARG_STR: fprintf(file, "char*"); break;
318 case ARG_WSTR: fprintf(file, "wchar_t*"); break;
319 case ARG_DBL: fprintf(file, "double"); break;
320 case ARG_INT64: fprintf(file, "__int64"); break;
321 /* __int128 is not supported on x86, so use a custom type */
322 case ARG_INT128: fprintf(file, "MyInt128"); break;
323 case ARG_FLOAT: fprintf(file, "float"); break;
324 }
325 fprintf(file, " a%d", i);
326 }
327
328 if (bInPrototype)
329 {
330 fprintf(file, ");\n\n");
331 }
332 } while (bInPrototype--);
333
334 if (!bRelay)
335 {
336 fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(",
337 pexp->strName.len, pexp->strName.buf);
338 }
339 else
340 {
341 fprintf(file, ")\n{\n");
342 if (pexp->uFlags & FL_REGISTER)
343 {
344 /* No return value */
345 }
346 else if (pexp->uFlags & FL_RET64)
347 {
348 fprintf(file, "\t__int64 retval;\n");
349 }
350 else
351 {
352 fprintf(file, "\tint retval;\n");
353 }
354 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(",
355 pszDllName, pexp->strName.len, pexp->strName.buf);
356 }
357
358 for (i = 0; i < pexp->nArgCount; i++)
359 {
360 if (i != 0) fprintf(file, ",");
361 switch (pexp->anArgs[i])
362 {
363 case ARG_LONG: fprintf(file, "0x%%lx"); break;
364 case ARG_PTR: fprintf(file, "0x%%p"); break;
365 case ARG_STR: fprintf(file, "'%%s'"); break;
366 case ARG_WSTR: fprintf(file, "'%%ws'"); break;
367 case ARG_DBL: fprintf(file, "%%f"); break;
368 case ARG_INT64: fprintf(file, "0x%%\"PRIx64\""); break;
369 case ARG_INT128: fprintf(file, "0x%%\"PRIx64\"-0x%%\"PRIx64\""); break;
370 case ARG_FLOAT: fprintf(file, "%%f"); break;
371 }
372 }
373 fprintf(file, ")\\n\"");
374
375 for (i = 0; i < pexp->nArgCount; i++)
376 {
377 fprintf(file, ", ");
378 switch (pexp->anArgs[i])
379 {
380 case ARG_LONG: case ARG_PTR: case ARG_STR:
381 case ARG_WSTR: case ARG_DBL: case ARG_INT64:
382 fprintf(file, "a%d", i); break;
383 case ARG_INT128:
384 fprintf(file, "a%d.lower, a%d.upper", i, i); break;
385 case ARG_FLOAT:
386 fprintf(file, "a%d", i); break;
387 }
388 }
389 fprintf(file, ");\n");
390
391 if (pexp->nCallingConvention == CC_STUB)
392 {
393 fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName);
394 }
395 else if (bRelay)
396 {
397 if (pexp->uFlags & FL_REGISTER)
398 {
399 fprintf(file,"\t");
400 }
401 else
402 {
403 fprintf(file, "\tretval = ");
404 }
405 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
406
407 for (i = 0; i < pexp->nArgCount; i++)
408 {
409 if (i != 0) fprintf(file, ", ");
410 fprintf(file, "a%d", i);
411 }
412 fprintf(file, ");\n");
413 }
414
415 if (!bRelay)
416 fprintf(file, "\treturn 0;\n}\n\n");
417 else if ((pexp->uFlags & FL_REGISTER) == 0)
418 {
419 if (pexp->uFlags & FL_RET64)
420 {
421 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%\"PRIx64\"\\n\", retval);\n",
422 pszDllName, pexp->strName.len, pexp->strName.buf);
423 }
424 else
425 {
426 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n",
427 pszDllName, pexp->strName.len, pexp->strName.buf);
428 }
429 fprintf(file, "\treturn retval;\n}\n\n");
430 }
431
432 return 1;
433 }
434
435 void
OutputHeader_asmstub(FILE * file,char * libname)436 OutputHeader_asmstub(FILE *file, char *libname)
437 {
438 fprintf(file, "; File generated automatically, do not edit!\n\n");
439
440 if (giArch == ARCH_X86)
441 {
442 fprintf(file, ".586\n.model flat\n.code\n");
443 }
444 else if (giArch == ARCH_AMD64)
445 {
446 fprintf(file, ".code\n");
447 }
448 else if (giArch == ARCH_ARM || giArch == ARCH_ARM64)
449 {
450 fprintf(file, " AREA |.text|,ALIGN=2,CODE,READONLY\n\n");
451 }
452 }
453
454 void
Output_stublabel(FILE * fileDest,char * pszSymbolName)455 Output_stublabel(FILE *fileDest, char* pszSymbolName)
456 {
457 if (giArch == ARCH_ARM || giArch == ARCH_ARM64)
458 {
459 fprintf(fileDest,
460 "\tEXPORT |%s| [FUNC]\n|%s|\n",
461 pszSymbolName,
462 pszSymbolName);
463 }
464 else
465 {
466 fprintf(fileDest,
467 "PUBLIC %s\n%s: nop\n",
468 pszSymbolName,
469 pszSymbolName);
470 }
471 }
472
473 int
OutputLine_asmstub(FILE * fileDest,EXPORT * pexp)474 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp)
475 {
476 char szNameBuffer[128];
477
478 /* Handle autoname */
479 if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@')
480 {
481 sprintf(szNameBuffer, "%s_stub_ordinal%d",
482 gpszUnderscore, pexp->nOrdinal);
483 }
484 else if (giArch != ARCH_X86)
485 {
486 /* Does the string already have stdcall decoration? */
487 const char *pcAt = ScanToken(pexp->strName.buf, '@');
488 if (pcAt && (pcAt < (pexp->strName.buf + pexp->strName.len)) &&
489 (pexp->strName.buf[0] == '_'))
490 {
491 /* Skip leading underscore and remove trailing decoration */
492 sprintf(szNameBuffer, "_stub_%.*s",
493 (int)(pcAt - pexp->strName.buf - 1),
494 pexp->strName.buf + 1);
495 }
496 else
497 {
498 sprintf(szNameBuffer, "_stub_%.*s",
499 pexp->strName.len, pexp->strName.buf);
500 }
501 }
502 else if (pexp->nCallingConvention == CC_STDCALL)
503 {
504 sprintf(szNameBuffer, "__stub_%.*s@%d",
505 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
506 }
507 else if (pexp->nCallingConvention == CC_FASTCALL)
508 {
509 sprintf(szNameBuffer, "@_stub_%.*s@%d",
510 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
511 }
512 else if ((pexp->nCallingConvention == CC_CDECL) ||
513 (pexp->nCallingConvention == CC_THISCALL) ||
514 (pexp->nCallingConvention == CC_EXTERN) ||
515 (pexp->nCallingConvention == CC_STUB))
516 {
517 sprintf(szNameBuffer, "__stub_%.*s",
518 pexp->strName.len, pexp->strName.buf);
519 }
520 else
521 {
522 fprintf(stderr, "Invalid calling convention");
523 return 0;
524 }
525
526 Output_stublabel(fileDest, szNameBuffer);
527
528 return 1;
529 }
530
531 void
OutputHeader_def(FILE * file,char * libname)532 OutputHeader_def(FILE *file, char *libname)
533 {
534 fprintf(file,
535 "; File generated automatically, do not edit!\n\n"
536 "NAME %s\n\n"
537 "EXPORTS\n",
538 libname);
539 }
540
541 void
PrintName(FILE * fileDest,EXPORT * pexp,PSTRING pstr,int fDeco)542 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
543 {
544 const char *pcName = pstr->buf;
545 int nNameLength = pstr->len;
546 const char* pcDot, *pcAt;
547 char namebuffer[19];
548
549 if ((nNameLength == 1) && (pcName[0] == '@'))
550 {
551 sprintf(namebuffer, "ordinal%d", pexp->nOrdinal);
552 pcName = namebuffer;
553 nNameLength = strlen(namebuffer);
554 }
555
556 /* Check for non-x86 first */
557 if (giArch != ARCH_X86)
558 {
559 /* Does the string already have stdcall decoration? */
560 pcAt = ScanToken(pcName, '@');
561 if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_'))
562 {
563 /* Skip leading underscore and remove trailing decoration */
564 pcName++;
565 nNameLength = (int)(pcAt - pcName);
566 }
567
568 /* Print the undecorated function name */
569 fprintf(fileDest, "%.*s", nNameLength, pcName);
570 }
571 else if (fDeco &&
572 ((pexp->nCallingConvention == CC_STDCALL) ||
573 (pexp->nCallingConvention == CC_FASTCALL)))
574 {
575 /* Beware with C++ exports */
576 int is_cpp = pcName[0] == '?';
577
578 /* Scan for a dll forwarding dot */
579 pcDot = ScanToken(pcName, '.');
580 if (pcDot)
581 {
582 /* First print the dll name, followed by a dot */
583 nNameLength = (int)(pcDot - pcName);
584 fprintf(fileDest, "%.*s.", nNameLength, pcName);
585
586 /* Now the actual function name */
587 pcName = pcDot + 1;
588 nNameLength = pexp->strTarget.len - nNameLength - 1;
589 }
590
591 /* Does the string already have decoration? */
592 pcAt = ScanToken(pcName, '@');
593 if (pcAt && (pcAt < (pcName + nNameLength)))
594 {
595 /* On GCC, we need to remove the leading stdcall underscore, but not for C++ exports */
596 if (!gbMSComp && !is_cpp && (pexp->nCallingConvention == CC_STDCALL))
597 {
598 pcName++;
599 nNameLength--;
600 }
601
602 /* Print the already decorated function name */
603 fprintf(fileDest, "%.*s", nNameLength, pcName);
604 }
605 else
606 {
607 /* Print the prefix, but skip it for (GCC && stdcall) */
608 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL))
609 {
610 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_');
611 }
612
613 /* Print the name with trailing decoration */
614 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes);
615 }
616 }
617 else if (fDeco && (pexp->nCallingConvention == CC_CDECL) && gbMSComp)
618 {
619 /* Print with cdecl decoration */
620 fprintf(fileDest, "_%.*s", nNameLength, pcName);
621 }
622 else
623 {
624 /* Print the undecorated function name */
625 fprintf(fileDest, "%.*s", nNameLength, pcName);
626 }
627 }
628
629 void
OutputLine_def_MS(FILE * fileDest,EXPORT * pexp)630 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
631 {
632 PrintName(fileDest, pexp, &pexp->strName, 0);
633
634 if (gbImportLib)
635 {
636 /* Redirect to a stub function, to get the right decoration in the lib */
637 fprintf(fileDest, "=_stub_");
638 PrintName(fileDest, pexp, &pexp->strName, 0);
639 }
640 else if (pexp->strTarget.buf)
641 {
642 if (pexp->strName.buf[0] == '?')
643 {
644 //fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n",
645 // pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
646 }
647 else
648 {
649 fprintf(fileDest, "=");
650
651 /* If the original name was decorated, use decoration in the forwarder as well */
652 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') &&
653 !ScanToken(pexp->strTarget.buf, '@') &&
654 ((pexp->nCallingConvention == CC_STDCALL) ||
655 (pexp->nCallingConvention == CC_FASTCALL)) )
656 {
657 PrintName(fileDest, pexp, &pexp->strTarget, 1);
658 }
659 else
660 {
661 /* Write the undecorated redirection name */
662 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf);
663 }
664 }
665 }
666 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
667 (pexp->strName.buf[0] == '?'))
668 {
669 /* C++ stubs are forwarded to C stubs */
670 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
671 }
672 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) &&
673 (pexp->strName.buf[0] != '?'))
674 {
675 /* Redirect it to the relay-tracing trampoline */
676 fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
677 }
678 }
679
680 void
OutputLine_def_GCC(FILE * fileDest,EXPORT * pexp)681 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
682 {
683 int bTracing = 0;
684 /* Print the function name, with decoration for export libs */
685 PrintName(fileDest, pexp, &pexp->strName, gbImportLib);
686 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf);
687
688 /* Check if this is a forwarded export */
689 if (pexp->strTarget.buf)
690 {
691 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.');
692 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
693
694 /* print the target name, don't decorate if it is external */
695 fprintf(fileDest, pexp->uFlags & FL_IMPSYM ? "==" : "=");
696 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal);
697 }
698 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
699 (pexp->strName.buf[0] == '?'))
700 {
701 /* C++ stubs are forwarded to C stubs */
702 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
703 }
704 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) &&
705 (pexp->nCallingConvention == CC_STDCALL) &&
706 (pexp->strName.buf[0] != '?'))
707 {
708 /* Redirect it to the relay-tracing trampoline */
709 char buf[256];
710 STRING strTarget;
711 fprintf(fileDest, "=");
712 sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
713 strTarget.buf = buf;
714 strTarget.len = pexp->strName.len + 12;
715 PrintName(fileDest, pexp, &strTarget, 1);
716 bTracing = 1;
717 }
718
719 /* Special handling for stdcall and fastcall, but not C++ exports*/
720 if ((giArch == ARCH_X86) &&
721 (pexp->strName.buf[0] != '?') &&
722 ((pexp->nCallingConvention == CC_STDCALL) ||
723 (pexp->nCallingConvention == CC_FASTCALL)))
724 {
725 /* Is this the import lib? */
726 if (gbImportLib)
727 {
728 /* Is the name in the spec file decorated? */
729 const char* pcDeco = ScanToken(pexp->strName.buf, '@');
730 if (pcDeco &&
731 (pexp->strName.len > 1) &&
732 (pcDeco < pexp->strName.buf + pexp->strName.len))
733 {
734 /* Write the name including the leading @ */
735 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
736 }
737 }
738 else if ((!pexp->strTarget.buf) && !(bTracing))
739 {
740 /* Write a forwarder to the actual decorated symbol */
741 fprintf(fileDest, "=");
742 PrintName(fileDest, pexp, &pexp->strName, 1);
743 }
744 }
745 }
746
747 int
OutputLine_def(FILE * fileDest,EXPORT * pexp)748 OutputLine_def(FILE *fileDest, EXPORT *pexp)
749 {
750 /* Don't add private exports to the import lib */
751 if (gbImportLib && (pexp->uFlags & FL_PRIVATE))
752 {
753 DbgPrint("OutputLine_def: skipping private export '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
754 return 1;
755 }
756
757 /* Handle import symbols */
758 if (pexp->uFlags & FL_IMPSYM)
759 {
760 /* Skip these, if we are not creating an import lib, or if this is MS */
761 if (!gbImportLib || gbMSComp)
762 {
763 DbgPrint("OutputLine_def: skipping import symbol '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
764 return 1;
765 }
766 }
767
768 /* For MS linker, forwarded externs are managed via #pragma comment(linker,"/export:_data=org.data,DATA") */
769 if (gbMSComp && !gbImportLib && (pexp->nCallingConvention == CC_EXTERN) &&
770 (pexp->strTarget.buf != NULL) && !!ScanToken(pexp->strTarget.buf, '.'))
771 {
772 DbgPrint("OutputLine_def: skipping forwarded extern export '%.*s' ->'%.*s'...\n",
773 pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
774 return 1;
775 }
776
777 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
778 fprintf(fileDest, " ");
779
780 if (gbMSComp)
781 OutputLine_def_MS(fileDest, pexp);
782 else
783 OutputLine_def_GCC(fileDest, pexp);
784
785 /* If it is not an import lib, we force ordinals */
786 if ((pexp->uFlags & FL_ORDINAL) || !gbImportLib)
787 {
788 fprintf(fileDest, " @%d", pexp->nOrdinal);
789 }
790
791 if (pexp->uFlags & FL_NONAME)
792 {
793 fprintf(fileDest, " NONAME");
794 }
795
796 /* Either PRIVATE or DATA */
797 if (pexp->uFlags & FL_PRIVATE)
798 {
799 fprintf(fileDest, " PRIVATE");
800 }
801 else if (pexp->nCallingConvention == CC_EXTERN)
802 {
803 fprintf(fileDest, " DATA");
804 }
805
806 fprintf(fileDest, "\n");
807
808 return 1;
809 }
810
811 void
PrintNameOrImpName(FILE * fileDest,EXPORT * pexp,PSTRING pstr,int fDeco,int fImp)812 PrintNameOrImpName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco, int fImp)
813 {
814 if (fImp)
815 {
816 fprintf(fileDest, "__imp_");
817 }
818
819 PrintName(fileDest, pexp, pstr, fDeco);
820 }
821
822 void
OutputAlias(FILE * fileDest,EXPORT * pexp,int fImp)823 OutputAlias(FILE *fileDest, EXPORT *pexp, int fImp)
824 {
825 if ((giArch == ARCH_ARM) || (giArch == ARCH_ARM64))
826 {
827 fprintf(fileDest, " IMPORT ");
828 PrintNameOrImpName(fileDest, pexp, &pexp->strName, 1, fImp);
829 fprintf(fileDest, ", WEAK ");
830 PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
831 fprintf(fileDest, "\n");
832 }
833 else
834 {
835 fprintf(fileDest, " EXTERN ");
836 PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
837 fprintf(fileDest, ":PROC\n ALIAS <");
838 PrintNameOrImpName(fileDest, pexp, &pexp->strName, 1, fImp);
839 fprintf(fileDest, "> = <");
840 PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
841 fprintf(fileDest, ">\n");
842 }
843 }
844
845 int
OutputLine_implib_asm(FILE * fileDest,EXPORT * pexp)846 OutputLine_implib_asm(FILE *fileDest, EXPORT *pexp)
847 {
848 if ((pexp->uFlags & FL_IMPSYM) == 0)
849 {
850 return 1;
851 }
852
853 OutputAlias(fileDest, pexp, 0);
854 OutputAlias(fileDest, pexp, 1);
855
856 return 1;
857 }
858
859 void
Fatalv(const char * filename,unsigned nLine,const char * pcLine,const char * pc,size_t errorlen,const char * format,va_list argptr)860 Fatalv(
861 const char* filename,
862 unsigned nLine,
863 const char *pcLine,
864 const char *pc,
865 size_t errorlen,
866 const char *format,
867 va_list argptr)
868 {
869 unsigned i, errorpos, len;
870 const char* pcLineEnd;
871
872 /* Get the length of the line */
873 pcLineEnd = strpbrk(pcLine, "\r\n");
874 len = (unsigned)(pcLineEnd - pcLine);
875
876 if (pc == NULL)
877 {
878 pc = pcLine + len - 1;
879 errorlen = 1;
880 }
881
882 errorpos = (unsigned)(pc - pcLine);
883
884 /* Output the error message */
885 fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos);
886 vfprintf(stderr, format, argptr);
887 fprintf(stderr, "\n");
888
889 /* Output the line with the error */
890 fprintf(stderr, "> %.*s\n", len, pcLine);
891
892 if (errorlen == 0)
893 {
894 errorlen = TokenLength(pc);
895 }
896
897 for (i = 0; i < errorpos + 2; i++)
898 {
899 fprintf(stderr, " ");
900 }
901 for (i = 0; i < errorlen; i++)
902 {
903 fprintf(stderr, "~");
904 }
905 fprintf(stderr, "\n");
906 exit(-1);
907 }
908
909 void
Fatal(const char * filename,unsigned nLine,const char * pcLine,const char * pc,size_t errorlen,const char * format,...)910 Fatal(
911 const char* filename,
912 unsigned nLine,
913 const char *pcLine,
914 const char *pc,
915 size_t errorlen,
916 const char *format,
917 ...)
918 {
919 va_list argptr;
920
921 va_start(argptr, format);
922 Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr);
923 va_end(argptr);
924 }
925
926 EXPORT *
ParseFile(char * pcStart,FILE * fileDest,unsigned * cExports)927 ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports)
928 {
929 EXPORT *pexports;
930 const char *pc, *pcLine;
931 int cLines, nLine;
932 EXPORT exp;
933 int included;
934 unsigned int i;
935
936 *cExports = 0;
937
938 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
939
940 /* Count the lines */
941 for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++)
942 {
943 /* Nothing */
944 }
945
946 /* Allocate an array of EXPORT structures */
947 pexports = malloc(cLines * sizeof(EXPORT));
948 if (pexports == NULL)
949 {
950 fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines);
951 return NULL;
952 }
953
954 /* Loop all lines */
955 nLine = 1;
956 exp.nNumber = 0;
957 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
958 {
959 pc = pcLine;
960
961 exp.strName.buf = NULL;
962 exp.strName.len = 0;
963 exp.strTarget.buf = NULL;
964 exp.strTarget.len = 0;
965 exp.nArgCount = 0;
966 exp.uFlags = 0;
967 exp.nNumber++;
968 exp.nStartVersion = 0;
969 exp.nEndVersion = 0xFFFFFFFF;
970 exp.bVersionIncluded = 1;
971
972 /* Skip white spaces */
973 while (*pc == ' ' || *pc == '\t') pc++;
974
975 /* Check for line break or comment */
976 if ((*pc == '\r') || (*pc == '\n') ||
977 (*pc == ';') || (*pc == '#'))
978 {
979 continue;
980 }
981
982 /* On EOF we are done */
983 if (*pc == 0)
984 {
985 return pexports;
986 }
987
988 /* Now we should get either an ordinal or @ */
989 if (*pc == '@')
990 {
991 exp.nOrdinal = -1;
992 }
993 else if ((*pc >= '0') && (*pc <= '9'))
994 {
995 char* end;
996 long int number = strtol(pc, &end, 10);
997 if ((*end != ' ') && (*end != '\t'))
998 {
999 Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal");
1000 }
1001
1002 if ((number < 0) || (number > 0xFFFE))
1003 {
1004 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal");
1005 }
1006
1007 exp.nOrdinal = number;
1008
1009 /* The import lib should contain the ordinal only if -ordinal was specified */
1010 if (!gbImportLib)
1011 exp.uFlags |= FL_ORDINAL;
1012 }
1013 else
1014 {
1015 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal");
1016 }
1017
1018 /* Go to next token (type) */
1019 if (!(pc = NextToken(pc)))
1020 {
1021 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1022 }
1023
1024 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
1025
1026 /* Now we should get the type */
1027 if (CompareToken(pc, "stdcall"))
1028 {
1029 exp.nCallingConvention = CC_STDCALL;
1030 }
1031 else if (CompareToken(pc, "cdecl") ||
1032 CompareToken(pc, "varargs"))
1033 {
1034 exp.nCallingConvention = CC_CDECL;
1035 }
1036 else if (CompareToken(pc, "fastcall"))
1037 {
1038 exp.nCallingConvention = CC_FASTCALL;
1039 }
1040 else if (CompareToken(pc, "thiscall"))
1041 {
1042 exp.nCallingConvention = CC_THISCALL;
1043 }
1044 else if (CompareToken(pc, "extern"))
1045 {
1046 exp.nCallingConvention = CC_EXTERN;
1047 }
1048 else if (CompareToken(pc, "stub"))
1049 {
1050 exp.nCallingConvention = CC_STUB;
1051 }
1052 else
1053 {
1054 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention");
1055 }
1056
1057 /* Go to next token (options or name) */
1058 if (!(pc = NextToken(pc)))
1059 {
1060 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1061 }
1062
1063 /* Handle options */
1064 included = 1;
1065 while (*pc == '-')
1066 {
1067 if (CompareToken(pc, "-arch="))
1068 {
1069 /* Default to not included */
1070 included = 0;
1071 pc += 5;
1072
1073 /* Look if we are included */
1074 do
1075 {
1076 int negated = 0, match = 0;
1077
1078 pc++;
1079
1080 /* Check for negated case */
1081 if (*pc == '!')
1082 {
1083 negated = 1;
1084 pc++;
1085 }
1086
1087 if (CompareToken(pc, pszArchString) ||
1088 CompareToken(pc, pszArchString2))
1089 {
1090 match = 1;
1091 }
1092
1093 if (match != negated)
1094 {
1095 included = 1;
1096 }
1097
1098 /* Skip to next arch or end */
1099 while (*pc > ',') pc++;
1100 } while (*pc == ',');
1101 }
1102 else if (CompareToken(pc, "-i386"))
1103 {
1104 if (giArch != ARCH_X86) included = 0;
1105 }
1106 else if (CompareToken(pc, "-version="))
1107 {
1108 const char *pcVersionStart = pc + 9;
1109
1110 /* Default to not included */
1111 exp.bVersionIncluded = 0;
1112 pc += 8;
1113
1114 /* Look if we are included */
1115 do
1116 {
1117 unsigned version, endversion;
1118
1119 /* Optionally skip leading '0x' */
1120 pc++;
1121 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1122
1123 /* Now get the version number */
1124 endversion = version = strtoul(pc, (char**)&pc, 16);
1125
1126 /* Check if it's a range */
1127 if (pc[0] == '+')
1128 {
1129 endversion = 0xFFF;
1130 pc++;
1131 }
1132 else if (pc[0] == '-')
1133 {
1134 /* Optionally skip leading '0x' */
1135 pc++;
1136 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1137 endversion = strtoul(pc, (char**)&pc, 16);
1138 }
1139
1140 /* Check for degenerate range */
1141 if (version > endversion)
1142 {
1143 Fatal(pszSourceFileName,
1144 nLine,
1145 pcLine,
1146 pcVersionStart,
1147 pc - pcVersionStart,
1148 "Invalid version range");
1149 }
1150
1151 exp.nStartVersion = version;
1152 exp.nEndVersion = endversion;
1153
1154 /* Now compare the range with our version */
1155 if ((guOsVersion >= version) &&
1156 (guOsVersion <= endversion))
1157 {
1158 exp.bVersionIncluded = 1;
1159 }
1160
1161 /* Skip to next arch or end */
1162 while (*pc > ',') pc++;
1163
1164 } while (*pc == ',');
1165 }
1166 else if (CompareToken(pc, "-dbg"))
1167 {
1168 if (!gbDbgExports)
1169 {
1170 included = 0;
1171 }
1172 }
1173 else if (CompareToken(pc, "-private"))
1174 {
1175 exp.uFlags |= FL_PRIVATE;
1176 }
1177 else if (CompareToken(pc, "-noname"))
1178 {
1179 exp.uFlags |= FL_ORDINAL | FL_NONAME;
1180 }
1181 else if (CompareToken(pc, "-impsym"))
1182 {
1183 exp.uFlags |= FL_IMPSYM;
1184 }
1185 else if (CompareToken(pc, "-ordinal"))
1186 {
1187 exp.uFlags |= FL_ORDINAL;
1188 /* GCC doesn't automatically import by ordinal if an ordinal
1189 * is found in the def file. Force it. */
1190 if (gbImportLib && !gbMSComp)
1191 exp.uFlags |= FL_NONAME;
1192 }
1193 else if (CompareToken(pc, "-stub"))
1194 {
1195 exp.uFlags |= FL_STUB;
1196 }
1197 else if (CompareToken(pc, "-norelay"))
1198 {
1199 exp.uFlags |= FL_NORELAY;
1200 }
1201 else if (CompareToken(pc, "-ret64"))
1202 {
1203 exp.uFlags |= FL_RET64;
1204 }
1205 else if (CompareToken(pc, "-register"))
1206 {
1207 exp.uFlags |= FL_REGISTER;
1208 }
1209 else
1210 {
1211 fprintf(stdout,
1212 "INFO: %s line %d: Ignored option: '%.*s'\n",
1213 pszSourceFileName,
1214 nLine,
1215 TokenLength(pc),
1216 pc);
1217 }
1218
1219 /* Go to next token */
1220 pc = NextToken(pc);
1221 }
1222
1223 //fprintf(stderr, "info: Name:'%.10s'\n", pc);
1224
1225 /* If arch didn't match ours, skip this entry */
1226 if (!included) continue;
1227
1228 /* Get name */
1229 exp.strName.buf = pc;
1230 exp.strName.len = TokenLength(pc);
1231 //DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
1232
1233 /* Check for autoname */
1234 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
1235 {
1236 exp.uFlags |= FL_ORDINAL | FL_NONAME;
1237 }
1238
1239 /* Handle parameters */
1240 exp.nStackBytes = 0;
1241 pc = NextToken(pc);
1242 /* Extern can't have parameters, and it's optional to provide ones for stubs. All other exports must have them */
1243 if (!pc && (exp.nCallingConvention != CC_EXTERN && exp.nCallingConvention != CC_STUB))
1244 {
1245 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1246 }
1247
1248 if (pc && (exp.nCallingConvention != CC_EXTERN))
1249 {
1250 /* Verify syntax */
1251 if (*pc++ != '(')
1252 {
1253 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('");
1254 }
1255
1256 /* Skip whitespaces */
1257 while (*pc == ' ' || *pc == '\t') pc++;
1258
1259 exp.nStackBytes = 0;
1260 while (*pc >= '0')
1261 {
1262 if (CompareToken(pc, "long"))
1263 {
1264 exp.nStackBytes += 4;
1265 exp.anArgs[exp.nArgCount] = ARG_LONG;
1266 }
1267 else if (CompareToken(pc, "double"))
1268 {
1269 exp.nStackBytes += 8;
1270 exp.anArgs[exp.nArgCount] = ARG_DBL;
1271 }
1272 else if (CompareToken(pc, "ptr"))
1273 {
1274 exp.nStackBytes += 4; // sizeof(void*) on x86
1275 exp.anArgs[exp.nArgCount] = ARG_PTR;
1276 }
1277 else if (CompareToken(pc, "str"))
1278 {
1279 exp.nStackBytes += 4; // sizeof(void*) on x86
1280 exp.anArgs[exp.nArgCount] = ARG_STR;
1281 }
1282 else if (CompareToken(pc, "wstr"))
1283 {
1284 exp.nStackBytes += 4; // sizeof(void*) on x86
1285 exp.anArgs[exp.nArgCount] = ARG_WSTR;
1286 }
1287 else if (CompareToken(pc, "int64"))
1288 {
1289 exp.nStackBytes += 8;
1290 exp.anArgs[exp.nArgCount] = ARG_INT64;
1291 }
1292 else if (CompareToken(pc, "int128"))
1293 {
1294 exp.nStackBytes += 16;
1295 exp.anArgs[exp.nArgCount] = ARG_INT128;
1296 }
1297 else if (CompareToken(pc, "float"))
1298 {
1299 exp.nStackBytes += 4;
1300 exp.anArgs[exp.nArgCount] = ARG_FLOAT;
1301 }
1302 else
1303 {
1304 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type");
1305 }
1306
1307 exp.nArgCount++;
1308
1309 /* Go to next parameter */
1310 if (!(pc = NextToken(pc)))
1311 {
1312 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1313 }
1314 }
1315
1316 /* Check syntax */
1317 if (*pc++ != ')')
1318 {
1319 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'");
1320 }
1321
1322 /* Go to next token */
1323 pc = NextToken(pc);
1324 }
1325
1326 /* Handle special stub cases */
1327 if (exp.nCallingConvention == CC_STUB)
1328 {
1329 /* If we got parameters, assume STDCALL */
1330 if (exp.nArgCount != 0)
1331 {
1332 exp.nCallingConvention = CC_STDCALL;
1333 exp.uFlags |= FL_STUB;
1334 }
1335
1336 /* Check for c++ mangled name */
1337 if (exp.strName.buf[0] == '?')
1338 {
1339 //printf("Found c++ mangled name...\n");
1340 //
1341 }
1342 else
1343 {
1344 /* Check for stdcall name */
1345 const char *p = ScanToken(exp.strName.buf, '@');
1346 if (p && (p - exp.strName.buf < exp.strName.len))
1347 {
1348 int i;
1349
1350 /* Truncate the name to before the @ */
1351 exp.strName.len = (int)(p - exp.strName.buf);
1352 if (exp.strName.len < 1)
1353 {
1354 Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @");
1355 }
1356 exp.nStackBytes = atoi(p + 1);
1357 exp.nArgCount = exp.nStackBytes / 4;
1358 exp.nCallingConvention = CC_STDCALL;
1359 exp.uFlags |= FL_STUB;
1360 for (i = 0; i < exp.nArgCount; i++)
1361 exp.anArgs[i] = ARG_LONG;
1362 }
1363 }
1364 }
1365
1366 /* Check optional redirection */
1367 if (pc)
1368 {
1369 exp.strTarget.buf = pc;
1370 exp.strTarget.len = TokenLength(pc);
1371
1372 /* Check syntax (end of line) */
1373 if (NextToken(pc))
1374 {
1375 Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition");
1376 }
1377
1378 /* Don't relay-trace forwarded functions */
1379 exp.uFlags |= FL_NORELAY;
1380 }
1381 else
1382 {
1383 exp.strTarget.buf = NULL;
1384 exp.strTarget.len = 0;
1385 }
1386
1387 /* Check for no-name without ordinal */
1388 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
1389 {
1390 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal");
1391 }
1392
1393 /* Check for import symbol without target */
1394 if ((exp.uFlags & FL_IMPSYM) && (exp.strTarget.buf == NULL))
1395 {
1396 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Import symbol without target");
1397 }
1398
1399 /*
1400 * Check for special handling of OLE exports, only when MSVC
1401 * is not used, since otherwise this is handled by MS LINK.EXE.
1402 */
1403 if (!gbMSComp)
1404 {
1405 /* Check whether the current export is not PRIVATE, or has an ordinal */
1406 int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE));
1407 int bHasOrdinal = (exp.uFlags & FL_ORDINAL);
1408
1409 /* Check whether the current export is an OLE export, in case any of these tests pass */
1410 if (bIsNotPrivate || bHasOrdinal)
1411 {
1412 for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i)
1413 {
1414 if (strlen(astrOlePrivateExports[i]) == exp.strName.len &&
1415 strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0)
1416 {
1417 /* The current export is an OLE export: display the corresponding warning */
1418 if (bIsNotPrivate)
1419 {
1420 fprintf(stderr, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n",
1421 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1422 }
1423 if (bHasOrdinal)
1424 {
1425 fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n",
1426 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1427 }
1428 break;
1429 }
1430 }
1431 }
1432 }
1433
1434 pexports[*cExports] = exp;
1435 (*cExports)++;
1436 gbDebug = 0;
1437 }
1438
1439 return pexports;
1440 }
1441
1442 int
ApplyOrdinals(EXPORT * pexports,unsigned cExports)1443 ApplyOrdinals(EXPORT* pexports, unsigned cExports)
1444 {
1445 unsigned short i, j;
1446 char* used;
1447 unsigned short firstOrdinal = 0xFFFF, firstIndex = 0;
1448
1449 /* Allocate a table to mark used ordinals */
1450 used = malloc(65536);
1451 if (used == NULL)
1452 {
1453 fprintf(stderr, "Failed to allocate memory for ordinal use table\n");
1454 return -1;
1455 }
1456 memset(used, 0, 65536);
1457
1458 /* Pass 1: mark the ordinals that are already used */
1459 for (i = 0; i < cExports; i++)
1460 {
1461 if (pexports[i].uFlags & FL_ORDINAL)
1462 {
1463 if (used[pexports[i].nOrdinal] != 0)
1464 {
1465 fprintf(stderr, "Found duplicate ordinal: %u\n", pexports[i].nOrdinal);
1466 return -1;
1467 }
1468 used[pexports[i].nOrdinal] = 1;
1469 if (pexports[i].nOrdinal < firstOrdinal)
1470 {
1471 firstOrdinal = pexports[i].nOrdinal;
1472 firstIndex = i;
1473 }
1474 }
1475 }
1476
1477 /* Check if we found an ordinal and it's larger than it's index */
1478 if ((firstOrdinal != 0xFFFF) && (firstOrdinal > firstIndex))
1479 {
1480 /* We did. Calculate an appropriate starting ordinal. */
1481 firstOrdinal -= firstIndex;
1482 }
1483 else
1484 {
1485 /* We didn't, so start with 1 */
1486 firstOrdinal = 1;
1487 }
1488
1489 /* Pass 2: apply available ordinals */
1490 for (i = 0, j = firstOrdinal; i < cExports; i++)
1491 {
1492 if ((pexports[i].uFlags & FL_ORDINAL) == 0 && pexports[i].bVersionIncluded)
1493 {
1494 while (used[j] != 0)
1495 j++;
1496
1497 pexports[i].nOrdinal = j;
1498 used[j] = 1;
1499 }
1500 }
1501
1502 free(used);
1503 return 0;
1504 }
1505
usage(void)1506 void usage(void)
1507 {
1508 printf("syntax: spec2def [<options> ...] <spec file>\n"
1509 "Possible options:\n"
1510 " -h --help print this help screen\n"
1511 " -l=<file> generate an asm lib stub\n"
1512 " -d=<file> generate a def file\n"
1513 " -s=<file> generate a stub file\n"
1514 " -i=<file> generate an import alias file\n"
1515 " --ms MSVC compatibility\n"
1516 " --dbg Enable debug exports\n"
1517 " -n=<name> name of the dll\n"
1518 " --version=<version> Sets the version to create exports for\n"
1519 " --implib generate a def file for an import library\n"
1520 " --no-private-warnings suppress warnings about symbols that should be -private\n"
1521 " -a=<arch> set architecture to <arch> (i386, x86_64, arm, arm64)\n"
1522 " --with-tracing generate wine-like \"+relay\" trace trampolines (needs -s)\n");
1523 }
1524
main(int argc,char * argv[])1525 int main(int argc, char *argv[])
1526 {
1527 size_t nFileSize;
1528 char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL;
1529 char *pszImpLibAliasFileName = NULL;
1530 const char* pszVersionOption = "--version=0x";
1531 char achDllName[40];
1532 FILE *file;
1533 unsigned cExports = 0, i;
1534 EXPORT *pexports;
1535
1536 if (argc < 2)
1537 {
1538 usage();
1539 return -1;
1540 }
1541
1542 /* Read options */
1543 for (i = 1; i < (unsigned)argc && *argv[i] == '-'; i++)
1544 {
1545 if ((strcasecmp(argv[i], "--help") == 0) ||
1546 (strcasecmp(argv[i], "-h") == 0))
1547 {
1548 usage();
1549 return 0;
1550 }
1551 else if (argv[i][1] == 'd' && argv[i][2] == '=')
1552 {
1553 pszDefFileName = argv[i] + 3;
1554 }
1555 else if (argv[i][1] == 'l' && argv[i][2] == '=')
1556 {
1557 pszLibStubName = argv[i] + 3;
1558 }
1559 else if (argv[i][1] == 's' && argv[i][2] == '=')
1560 {
1561 pszStubFileName = argv[i] + 3;
1562 }
1563 else if (argv[i][1] == 'i' && argv[i][2] == '=')
1564 {
1565 pszImpLibAliasFileName = argv[i] + 3;
1566 }
1567 else if (argv[i][1] == 'n' && argv[i][2] == '=')
1568 {
1569 pszDllName = argv[i] + 3;
1570 }
1571 else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0)
1572 {
1573 guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16);
1574 }
1575 else if (strcasecmp(argv[i], "--implib") == 0)
1576 {
1577 gbImportLib = 1;
1578 }
1579 else if (strcasecmp(argv[i], "--ms") == 0)
1580 {
1581 gbMSComp = 1;
1582 }
1583 else if (strcasecmp(argv[i], "--dbg") == 0)
1584 {
1585 gbDbgExports = 1;
1586 }
1587 else if (strcasecmp(argv[i], "--no-private-warnings") == 0)
1588 {
1589 gbNotPrivateNoWarn = 1;
1590 }
1591 else if (strcasecmp(argv[i], "--with-tracing") == 0)
1592 {
1593 if (!pszStubFileName)
1594 {
1595 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n");
1596 return -1;
1597 }
1598 gbTracing = 1;
1599 }
1600 else if (argv[i][1] == 'a' && argv[i][2] == '=')
1601 {
1602 pszArchString = argv[i] + 3;
1603 }
1604 else
1605 {
1606 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
1607 return -1;
1608 }
1609 }
1610
1611 if (strcasecmp(pszArchString, "i386") == 0)
1612 {
1613 giArch = ARCH_X86;
1614 gpszUnderscore = "_";
1615 }
1616 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
1617 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
1618 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
1619 else if (strcasecmp(pszArchString, "arm64") == 0) giArch = ARCH_ARM64;
1620 else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
1621
1622 if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
1623 {
1624 pszArchString2 = "win64";
1625 }
1626 else
1627 pszArchString2 = "win32";
1628
1629 /* Set a default dll name */
1630 if (!pszDllName)
1631 {
1632 char *p1, *p2;
1633 size_t len;
1634
1635 p1 = strrchr(argv[i], '\\');
1636 if (!p1) p1 = strrchr(argv[i], '/');
1637 p2 = p1 = p1 ? p1 + 1 : argv[i];
1638
1639 /* walk up to '.' */
1640 while (*p2 != '.' && *p2 != 0) p2++;
1641 len = p2 - p1;
1642 if (len >= sizeof(achDllName) - 5)
1643 {
1644 fprintf(stderr, "name too long: %s\n", p1);
1645 return -2;
1646 }
1647
1648 strncpy(achDllName, p1, len);
1649 strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
1650 pszDllName = achDllName;
1651 }
1652
1653 /* Open input file */
1654 pszSourceFileName = argv[i];
1655 file = fopen(pszSourceFileName, "r");
1656 if (!file)
1657 {
1658 fprintf(stderr, "error: could not open file %s\n", pszSourceFileName);
1659 return -3;
1660 }
1661
1662 /* Get file size */
1663 fseek(file, 0, SEEK_END);
1664 nFileSize = ftell(file);
1665 rewind(file);
1666
1667 /* Allocate memory buffer */
1668 pszSource = malloc(nFileSize + 1);
1669 if (!pszSource)
1670 {
1671 fclose(file);
1672 return -4;
1673 }
1674
1675 /* Load input file into memory */
1676 nFileSize = fread(pszSource, 1, nFileSize, file);
1677 fclose(file);
1678
1679 /* Zero terminate the source */
1680 pszSource[nFileSize] = '\0';
1681
1682 pexports = ParseFile(pszSource, file, &cExports);
1683 if (pexports == NULL)
1684 {
1685 fprintf(stderr, "error: could not parse file!\n");
1686 return -1;
1687 }
1688
1689 if (ApplyOrdinals(pexports, cExports) < 0)
1690 {
1691 fprintf(stderr, "error: could not apply ordinals!\n");
1692 return -1;
1693 }
1694
1695 if (pszDefFileName)
1696 {
1697 /* Open output file */
1698 file = fopen(pszDefFileName, "w");
1699 if (!file)
1700 {
1701 fprintf(stderr, "error: could not open output file %s\n", pszDefFileName);
1702 return -5;
1703 }
1704
1705 OutputHeader_def(file, pszDllName);
1706
1707 for (i = 0; i < cExports; i++)
1708 {
1709 if (pexports[i].bVersionIncluded)
1710 OutputLine_def(file, &pexports[i]);
1711 }
1712
1713 fclose(file);
1714 }
1715
1716 if (pszStubFileName)
1717 {
1718 /* Open output file */
1719 file = fopen(pszStubFileName, "w");
1720 if (!file)
1721 {
1722 fprintf(stderr, "error: could not open output file %s\n", pszStubFileName);
1723 return -5;
1724 }
1725
1726 OutputHeader_stub(file);
1727
1728 for (i = 0; i < cExports; i++)
1729 {
1730 if (pexports[i].bVersionIncluded)
1731 OutputLine_stub(file, &pexports[i]);
1732 }
1733
1734 fclose(file);
1735 }
1736
1737 if (pszLibStubName)
1738 {
1739 /* Open output file */
1740 file = fopen(pszLibStubName, "w");
1741 if (!file)
1742 {
1743 fprintf(stderr, "error: could not open output file %s\n", pszLibStubName);
1744 return -5;
1745 }
1746
1747 OutputHeader_asmstub(file, pszDllName);
1748
1749 for (i = 0; i < cExports; i++)
1750 {
1751 if (pexports[i].bVersionIncluded)
1752 OutputLine_asmstub(file, &pexports[i]);
1753 }
1754
1755 fprintf(file, "\n END\n");
1756 fclose(file);
1757 }
1758
1759 if (pszImpLibAliasFileName)
1760 {
1761 /* Open output file */
1762 file = fopen(pszImpLibAliasFileName, "w");
1763 if (!file)
1764 {
1765 fprintf(stderr, "error: could not open output file %s\n", pszImpLibAliasFileName);
1766 return -5;
1767 }
1768
1769 OutputHeader_asmstub(file, pszDllName);
1770
1771 for (i = 0; i < cExports; i++)
1772 {
1773 if (pexports[i].bVersionIncluded)
1774 OutputLine_implib_asm(file, &pexports[i]);
1775 }
1776
1777 fprintf(file, "\n END\n");
1778 fclose(file);
1779 }
1780
1781 free(pexports);
1782
1783 return 0;
1784 }
1785