xref: /reactos/sdk/tools/spec2def/spec2def.c (revision dc25409c)
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