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