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