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