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