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 /* Scan for a dll forwarding dot */ 580 pcDot = ScanToken(pcName, '.'); 581 if (pcDot) 582 { 583 /* First print the dll name, followed by a dot */ 584 nNameLength = (int)(pcDot - pcName); 585 fprintf(fileDest, "%.*s.", nNameLength, pcName); 586 587 /* Now the actual function name */ 588 pcName = pcDot + 1; 589 nNameLength = pexp->strTarget.len - nNameLength - 1; 590 } 591 592 /* Does the string already have decoration? */ 593 pcAt = ScanToken(pcName, '@'); 594 if (pcAt && (pcAt < (pcName + nNameLength))) 595 { 596 /* On GCC, we need to remove the leading stdcall underscore */ 597 if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL)) 598 { 599 pcName++; 600 nNameLength--; 601 } 602 603 /* Print the already decorated function name */ 604 fprintf(fileDest, "%.*s", nNameLength, pcName); 605 } 606 else 607 { 608 /* Print the prefix, but skip it for (GCC && stdcall) */ 609 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL)) 610 { 611 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_'); 612 } 613 614 /* Print the name with trailing decoration */ 615 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes); 616 } 617 } 618 else 619 { 620 /* Print the undecorated function name */ 621 fprintf(fileDest, "%.*s", nNameLength, pcName); 622 } 623 } 624 625 void 626 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp) 627 { 628 PrintName(fileDest, pexp, &pexp->strName, 0); 629 630 if (gbImportLib) 631 { 632 /* Redirect to a stub function, to get the right decoration in the lib */ 633 fprintf(fileDest, "=_stub_"); 634 PrintName(fileDest, pexp, &pexp->strName, 0); 635 } 636 else if (pexp->strTarget.buf) 637 { 638 if (pexp->strName.buf[0] == '?') 639 { 640 //fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n", 641 // pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf); 642 } 643 else 644 { 645 fprintf(fileDest, "="); 646 647 /* If the original name was decorated, use decoration in the forwarder as well */ 648 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') && 649 !ScanToken(pexp->strTarget.buf, '@') && 650 ((pexp->nCallingConvention == CC_STDCALL) || 651 (pexp->nCallingConvention == CC_FASTCALL)) ) 652 { 653 PrintName(fileDest, pexp, &pexp->strTarget, 1); 654 } 655 else 656 { 657 /* Write the undecorated redirection name */ 658 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf); 659 } 660 } 661 } 662 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 663 (pexp->strName.buf[0] == '?')) 664 { 665 /* C++ stubs are forwarded to C stubs */ 666 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 667 } 668 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) && 669 (pexp->strName.buf[0] != '?')) 670 { 671 /* Redirect it to the relay-tracing trampoline */ 672 fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 673 } 674 } 675 676 void 677 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp) 678 { 679 int bTracing = 0; 680 /* Print the function name, with decoration for export libs */ 681 PrintName(fileDest, pexp, &pexp->strName, gbImportLib); 682 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf); 683 684 /* Check if this is a forwarded export */ 685 if (pexp->strTarget.buf) 686 { 687 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.'); 688 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf); 689 690 /* print the target name, don't decorate if it is external */ 691 fprintf(fileDest, "="); 692 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal); 693 } 694 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 695 (pexp->strName.buf[0] == '?')) 696 { 697 /* C++ stubs are forwarded to C stubs */ 698 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 699 } 700 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && 701 (pexp->nCallingConvention == CC_STDCALL) && 702 (pexp->strName.buf[0] != '?')) 703 { 704 /* Redirect it to the relay-tracing trampoline */ 705 char buf[256]; 706 STRING strTarget; 707 fprintf(fileDest, "="); 708 sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 709 strTarget.buf = buf; 710 strTarget.len = pexp->strName.len + 12; 711 PrintName(fileDest, pexp, &strTarget, 1); 712 bTracing = 1; 713 } 714 715 /* Special handling for stdcall and fastcall */ 716 if ((giArch == ARCH_X86) && 717 ((pexp->nCallingConvention == CC_STDCALL) || 718 (pexp->nCallingConvention == CC_FASTCALL))) 719 { 720 /* Is this the import lib? */ 721 if (gbImportLib) 722 { 723 /* Is the name in the spec file decorated? */ 724 const char* pcDeco = ScanToken(pexp->strName.buf, '@'); 725 if (pcDeco && 726 (pexp->strName.len > 1) && 727 (pcDeco < pexp->strName.buf + pexp->strName.len)) 728 { 729 /* Write the name including the leading @ */ 730 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf); 731 } 732 } 733 else if ((!pexp->strTarget.buf) && !(bTracing)) 734 { 735 /* Write a forwarder to the actual decorated symbol */ 736 fprintf(fileDest, "="); 737 PrintName(fileDest, pexp, &pexp->strName, 1); 738 } 739 } 740 } 741 742 int 743 OutputLine_def(FILE *fileDest, EXPORT *pexp) 744 { 745 /* Don't add private exports to the import lib */ 746 if (gbImportLib && (pexp->uFlags & FL_PRIVATE)) 747 { 748 DbgPrint("OutputLine_def: skipping private export '%.*s'...\n", pexp->strName.len, pexp->strName.buf); 749 return 1; 750 } 751 752 /* For MS linker, forwarded externs are managed via #pragma comment(linker,"/export:_data=org.data,DATA") */ 753 if (gbMSComp && !gbImportLib && (pexp->nCallingConvention == CC_EXTERN) && 754 (pexp->strTarget.buf != NULL) && !!ScanToken(pexp->strTarget.buf, '.')) 755 { 756 DbgPrint("OutputLine_def: skipping forwarded extern export '%.*s' ->'%.*s'...\n", 757 pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf); 758 return 1; 759 } 760 761 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf); 762 fprintf(fileDest, " "); 763 764 if (gbMSComp) 765 OutputLine_def_MS(fileDest, pexp); 766 else 767 OutputLine_def_GCC(fileDest, pexp); 768 769 /* On GCC builds we force ordinals */ 770 if ((pexp->uFlags & FL_ORDINAL) || (!gbMSComp && !gbImportLib)) 771 { 772 fprintf(fileDest, " @%d", pexp->nOrdinal); 773 } 774 775 if (pexp->uFlags & FL_NONAME) 776 { 777 fprintf(fileDest, " NONAME"); 778 } 779 780 /* Either PRIVATE or DATA */ 781 if (pexp->uFlags & FL_PRIVATE) 782 { 783 fprintf(fileDest, " PRIVATE"); 784 } 785 else if (pexp->nCallingConvention == CC_EXTERN) 786 { 787 fprintf(fileDest, " DATA"); 788 } 789 790 fprintf(fileDest, "\n"); 791 792 return 1; 793 } 794 795 void 796 Fatalv( 797 const char* filename, 798 unsigned nLine, 799 const char *pcLine, 800 const char *pc, 801 size_t errorlen, 802 const char *format, 803 va_list argptr) 804 { 805 unsigned i, errorpos, len; 806 const char* pcLineEnd; 807 808 /* Get the length of the line */ 809 pcLineEnd = strpbrk(pcLine, "\r\n"); 810 len = (unsigned)(pcLineEnd - pcLine); 811 812 if (pc == NULL) 813 { 814 pc = pcLine + len - 1; 815 errorlen = 1; 816 } 817 818 errorpos = (unsigned)(pc - pcLine); 819 820 /* Output the error message */ 821 fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos); 822 vfprintf(stderr, format, argptr); 823 fprintf(stderr, "\n"); 824 825 /* Output the line with the error */ 826 fprintf(stderr, "> %.*s\n", len, pcLine); 827 828 if (errorlen == 0) 829 { 830 errorlen = TokenLength(pc); 831 } 832 833 for (i = 0; i < errorpos + 2; i++) 834 { 835 fprintf(stderr, " "); 836 } 837 for (i = 0; i < errorlen; i++) 838 { 839 fprintf(stderr, "~"); 840 } 841 fprintf(stderr, "\n"); 842 exit(-1); 843 } 844 845 void 846 Fatal( 847 const char* filename, 848 unsigned nLine, 849 const char *pcLine, 850 const char *pc, 851 size_t errorlen, 852 const char *format, 853 ...) 854 { 855 va_list argptr; 856 857 va_start(argptr, format); 858 Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr); 859 va_end(argptr); 860 } 861 862 EXPORT * 863 ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports) 864 { 865 EXPORT *pexports; 866 const char *pc, *pcLine; 867 int cLines, nLine; 868 EXPORT exp; 869 int included; 870 unsigned int i; 871 872 *cExports = 0; 873 874 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart); 875 876 /* Count the lines */ 877 for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++) 878 { 879 /* Nothing */ 880 } 881 882 /* Allocate an array of EXPORT structures */ 883 pexports = malloc(cLines * sizeof(EXPORT)); 884 if (pexports == NULL) 885 { 886 fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines); 887 return NULL; 888 } 889 890 /* Loop all lines */ 891 nLine = 1; 892 exp.nNumber = 0; 893 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++) 894 { 895 pc = pcLine; 896 897 exp.strName.buf = NULL; 898 exp.strName.len = 0; 899 exp.strTarget.buf = NULL; 900 exp.strTarget.len = 0; 901 exp.nArgCount = 0; 902 exp.uFlags = 0; 903 exp.nNumber++; 904 exp.nStartVersion = 0; 905 exp.nEndVersion = 0xFFFFFFFF; 906 exp.bVersionIncluded = 1; 907 908 /* Skip white spaces */ 909 while (*pc == ' ' || *pc == '\t') pc++; 910 911 /* Check for line break or comment */ 912 if ((*pc == '\r') || (*pc == '\n') || 913 (*pc == ';') || (*pc == '#')) 914 { 915 continue; 916 } 917 918 /* On EOF we are done */ 919 if (*pc == 0) 920 { 921 return pexports; 922 } 923 924 /* Now we should get either an ordinal or @ */ 925 if (*pc == '@') 926 { 927 exp.nOrdinal = -1; 928 } 929 else if ((*pc >= '0') && (*pc <= '9')) 930 { 931 char* end; 932 long int number = strtol(pc, &end, 10); 933 if ((*end != ' ') && (*end != '\t')) 934 { 935 Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal"); 936 } 937 938 if ((number < 0) || (number > 0xFFFE)) 939 { 940 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal"); 941 } 942 943 exp.nOrdinal = number; 944 945 /* The import lib should contain the ordinal only if -ordinal was specified */ 946 if (!gbImportLib) 947 exp.uFlags |= FL_ORDINAL; 948 } 949 else 950 { 951 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal"); 952 } 953 954 /* Go to next token (type) */ 955 if (!(pc = NextToken(pc))) 956 { 957 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 958 } 959 960 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc); 961 962 /* Now we should get the type */ 963 if (CompareToken(pc, "stdcall")) 964 { 965 exp.nCallingConvention = CC_STDCALL; 966 } 967 else if (CompareToken(pc, "cdecl") || 968 CompareToken(pc, "varargs")) 969 { 970 exp.nCallingConvention = CC_CDECL; 971 } 972 else if (CompareToken(pc, "fastcall")) 973 { 974 exp.nCallingConvention = CC_FASTCALL; 975 } 976 else if (CompareToken(pc, "thiscall")) 977 { 978 exp.nCallingConvention = CC_THISCALL; 979 } 980 else if (CompareToken(pc, "extern")) 981 { 982 exp.nCallingConvention = CC_EXTERN; 983 } 984 else if (CompareToken(pc, "stub")) 985 { 986 exp.nCallingConvention = CC_STUB; 987 } 988 else 989 { 990 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention"); 991 } 992 993 /* Go to next token (options or name) */ 994 if (!(pc = NextToken(pc))) 995 { 996 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 997 } 998 999 /* Handle options */ 1000 included = 1; 1001 while (*pc == '-') 1002 { 1003 if (CompareToken(pc, "-arch=")) 1004 { 1005 /* Default to not included */ 1006 included = 0; 1007 pc += 5; 1008 1009 /* Look if we are included */ 1010 do 1011 { 1012 pc++; 1013 if (CompareToken(pc, pszArchString) || 1014 CompareToken(pc, pszArchString2)) 1015 { 1016 included = 1; 1017 } 1018 1019 /* Skip to next arch or end */ 1020 while (*pc > ',') pc++; 1021 } while (*pc == ','); 1022 } 1023 else if (CompareToken(pc, "-i386")) 1024 { 1025 if (giArch != ARCH_X86) included = 0; 1026 } 1027 else if (CompareToken(pc, "-version=")) 1028 { 1029 const char *pcVersionStart = pc + 9; 1030 1031 /* Default to not included */ 1032 exp.bVersionIncluded = 0; 1033 pc += 8; 1034 1035 /* Look if we are included */ 1036 do 1037 { 1038 unsigned version, endversion; 1039 1040 /* Optionally skip leading '0x' */ 1041 pc++; 1042 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 1043 1044 /* Now get the version number */ 1045 endversion = version = strtoul(pc, (char**)&pc, 16); 1046 1047 /* Check if it's a range */ 1048 if (pc[0] == '+') 1049 { 1050 endversion = 0xFFF; 1051 pc++; 1052 } 1053 else if (pc[0] == '-') 1054 { 1055 /* Optionally skip leading '0x' */ 1056 pc++; 1057 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 1058 endversion = strtoul(pc, (char**)&pc, 16); 1059 } 1060 1061 /* Check for degenerate range */ 1062 if (version > endversion) 1063 { 1064 Fatal(pszSourceFileName, 1065 nLine, 1066 pcLine, 1067 pcVersionStart, 1068 pc - pcVersionStart, 1069 "Invalid version range"); 1070 } 1071 1072 exp.nStartVersion = version; 1073 exp.nEndVersion = endversion; 1074 1075 /* Now compare the range with our version */ 1076 if ((guOsVersion >= version) && 1077 (guOsVersion <= endversion)) 1078 { 1079 exp.bVersionIncluded = 1; 1080 } 1081 1082 /* Skip to next arch or end */ 1083 while (*pc > ',') pc++; 1084 1085 } while (*pc == ','); 1086 } 1087 else if (CompareToken(pc, "-private")) 1088 { 1089 exp.uFlags |= FL_PRIVATE; 1090 } 1091 else if (CompareToken(pc, "-noname")) 1092 { 1093 exp.uFlags |= FL_ORDINAL | FL_NONAME; 1094 } 1095 else if (CompareToken(pc, "-ordinal")) 1096 { 1097 exp.uFlags |= FL_ORDINAL; 1098 /* GCC doesn't automatically import by ordinal if an ordinal 1099 * is found in the def file. Force it. */ 1100 if (gbImportLib && !gbMSComp) 1101 exp.uFlags |= FL_NONAME; 1102 } 1103 else if (CompareToken(pc, "-stub")) 1104 { 1105 exp.uFlags |= FL_STUB; 1106 } 1107 else if (CompareToken(pc, "-norelay")) 1108 { 1109 exp.uFlags |= FL_NORELAY; 1110 } 1111 else if (CompareToken(pc, "-ret64")) 1112 { 1113 exp.uFlags |= FL_RET64; 1114 } 1115 else if (CompareToken(pc, "-register")) 1116 { 1117 exp.uFlags |= FL_REGISTER; 1118 } 1119 else 1120 { 1121 fprintf(stdout, 1122 "INFO: %s line %d: Ignored option: '%.*s'\n", 1123 pszSourceFileName, 1124 nLine, 1125 TokenLength(pc), 1126 pc); 1127 } 1128 1129 /* Go to next token */ 1130 pc = NextToken(pc); 1131 } 1132 1133 //fprintf(stderr, "info: Name:'%.10s'\n", pc); 1134 1135 /* If arch didn't match ours, skip this entry */ 1136 if (!included) continue; 1137 1138 /* Get name */ 1139 exp.strName.buf = pc; 1140 exp.strName.len = TokenLength(pc); 1141 //DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf); 1142 1143 /* Check for autoname */ 1144 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@')) 1145 { 1146 exp.uFlags |= FL_ORDINAL | FL_NONAME; 1147 } 1148 1149 /* Handle parameters */ 1150 exp.nStackBytes = 0; 1151 pc = NextToken(pc); 1152 /* Extern can't have parameters, and it's optional to provide ones for stubs. All other exports must have them */ 1153 if (!pc && (exp.nCallingConvention != CC_EXTERN && exp.nCallingConvention != CC_STUB)) 1154 { 1155 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 1156 } 1157 1158 if (pc && (exp.nCallingConvention != CC_EXTERN)) 1159 { 1160 /* Verify syntax */ 1161 if (*pc++ != '(') 1162 { 1163 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('"); 1164 } 1165 1166 /* Skip whitespaces */ 1167 while (*pc == ' ' || *pc == '\t') pc++; 1168 1169 exp.nStackBytes = 0; 1170 while (*pc >= '0') 1171 { 1172 if (CompareToken(pc, "long")) 1173 { 1174 exp.nStackBytes += 4; 1175 exp.anArgs[exp.nArgCount] = ARG_LONG; 1176 } 1177 else if (CompareToken(pc, "double")) 1178 { 1179 exp.nStackBytes += 8; 1180 exp.anArgs[exp.nArgCount] = ARG_DBL; 1181 } 1182 else if (CompareToken(pc, "ptr")) 1183 { 1184 exp.nStackBytes += 4; // sizeof(void*) on x86 1185 exp.anArgs[exp.nArgCount] = ARG_PTR; 1186 } 1187 else if (CompareToken(pc, "str")) 1188 { 1189 exp.nStackBytes += 4; // sizeof(void*) on x86 1190 exp.anArgs[exp.nArgCount] = ARG_STR; 1191 } 1192 else if (CompareToken(pc, "wstr")) 1193 { 1194 exp.nStackBytes += 4; // sizeof(void*) on x86 1195 exp.anArgs[exp.nArgCount] = ARG_WSTR; 1196 } 1197 else if (CompareToken(pc, "int64")) 1198 { 1199 exp.nStackBytes += 8; 1200 exp.anArgs[exp.nArgCount] = ARG_INT64; 1201 } 1202 else if (CompareToken(pc, "int128")) 1203 { 1204 exp.nStackBytes += 16; 1205 exp.anArgs[exp.nArgCount] = ARG_INT128; 1206 } 1207 else if (CompareToken(pc, "float")) 1208 { 1209 exp.nStackBytes += 4; 1210 exp.anArgs[exp.nArgCount] = ARG_FLOAT; 1211 } 1212 else 1213 { 1214 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type"); 1215 } 1216 1217 exp.nArgCount++; 1218 1219 /* Go to next parameter */ 1220 if (!(pc = NextToken(pc))) 1221 { 1222 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 1223 } 1224 } 1225 1226 /* Check syntax */ 1227 if (*pc++ != ')') 1228 { 1229 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'"); 1230 } 1231 1232 /* Go to next token */ 1233 pc = NextToken(pc); 1234 } 1235 1236 /* Handle special stub cases */ 1237 if (exp.nCallingConvention == CC_STUB) 1238 { 1239 /* If we got parameters, assume STDCALL */ 1240 if (exp.nArgCount != 0) 1241 { 1242 exp.nCallingConvention = CC_STDCALL; 1243 exp.uFlags |= FL_STUB; 1244 } 1245 1246 /* Check for c++ mangled name */ 1247 if (exp.strName.buf[0] == '?') 1248 { 1249 //printf("Found c++ mangled name...\n"); 1250 // 1251 } 1252 else 1253 { 1254 /* Check for stdcall name */ 1255 const char *p = ScanToken(exp.strName.buf, '@'); 1256 if (p && (p - exp.strName.buf < exp.strName.len)) 1257 { 1258 int i; 1259 1260 /* Truncate the name to before the @ */ 1261 exp.strName.len = (int)(p - exp.strName.buf); 1262 if (exp.strName.len < 1) 1263 { 1264 Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @"); 1265 } 1266 exp.nStackBytes = atoi(p + 1); 1267 exp.nArgCount = exp.nStackBytes / 4; 1268 exp.nCallingConvention = CC_STDCALL; 1269 exp.uFlags |= FL_STUB; 1270 for (i = 0; i < exp.nArgCount; i++) 1271 exp.anArgs[i] = ARG_LONG; 1272 } 1273 } 1274 } 1275 1276 /* Check optional redirection */ 1277 if (pc) 1278 { 1279 exp.strTarget.buf = pc; 1280 exp.strTarget.len = TokenLength(pc); 1281 1282 /* Check syntax (end of line) */ 1283 if (NextToken(pc)) 1284 { 1285 Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition"); 1286 } 1287 1288 /* Don't relay-trace forwarded functions */ 1289 exp.uFlags |= FL_NORELAY; 1290 } 1291 else 1292 { 1293 exp.strTarget.buf = NULL; 1294 exp.strTarget.len = 0; 1295 } 1296 1297 /* Check for no-name without ordinal */ 1298 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1)) 1299 { 1300 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal"); 1301 } 1302 1303 /* 1304 * Check for special handling of OLE exports, only when MSVC 1305 * is not used, since otherwise this is handled by MS LINK.EXE. 1306 */ 1307 if (!gbMSComp) 1308 { 1309 /* Check whether the current export is not PRIVATE, or has an ordinal */ 1310 int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE)); 1311 int bHasOrdinal = (exp.uFlags & FL_ORDINAL); 1312 1313 /* Check whether the current export is an OLE export, in case any of these tests pass */ 1314 if (bIsNotPrivate || bHasOrdinal) 1315 { 1316 for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i) 1317 { 1318 if (strlen(astrOlePrivateExports[i]) == exp.strName.len && 1319 strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0) 1320 { 1321 /* The current export is an OLE export: display the corresponding warning */ 1322 if (bIsNotPrivate) 1323 { 1324 fprintf(stderr, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n", 1325 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf); 1326 } 1327 if (bHasOrdinal) 1328 { 1329 fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n", 1330 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf); 1331 } 1332 break; 1333 } 1334 } 1335 } 1336 } 1337 1338 pexports[*cExports] = exp; 1339 (*cExports)++; 1340 gbDebug = 0; 1341 } 1342 1343 return pexports; 1344 } 1345 1346 int 1347 ApplyOrdinals(EXPORT* pexports, unsigned cExports) 1348 { 1349 unsigned short i, j; 1350 char* used; 1351 1352 /* Allocate a table to mark used ordinals */ 1353 used = malloc(65536); 1354 if (used == NULL) 1355 { 1356 fprintf(stderr, "Failed to allocate memory for ordinal use table\n"); 1357 return -1; 1358 } 1359 memset(used, 0, 65536); 1360 1361 /* Pass 1: mark the ordinals that are already used */ 1362 for (i = 0; i < cExports; i++) 1363 { 1364 if (pexports[i].uFlags & FL_ORDINAL) 1365 { 1366 if (used[pexports[i].nOrdinal] != 0) 1367 { 1368 fprintf(stderr, "Found duplicate ordinal: %u\n", pexports[i].nOrdinal); 1369 return -1; 1370 } 1371 used[pexports[i].nOrdinal] = 1; 1372 } 1373 } 1374 1375 /* Pass 2: apply available ordinals */ 1376 for (i = 0, j = 1; i < cExports; i++) 1377 { 1378 if ((pexports[i].uFlags & FL_ORDINAL) == 0 && pexports[i].bVersionIncluded) 1379 { 1380 while (used[j] != 0) 1381 j++; 1382 1383 pexports[i].nOrdinal = j; 1384 used[j] = 1; 1385 } 1386 } 1387 1388 free(used); 1389 return 0; 1390 } 1391 1392 void usage(void) 1393 { 1394 printf("syntax: spec2def [<options> ...] <spec file>\n" 1395 "Possible options:\n" 1396 " -h --help print this help screen\n" 1397 " -l=<file> generate an asm lib stub\n" 1398 " -d=<file> generate a def file\n" 1399 " -s=<file> generate a stub file\n" 1400 " --ms MSVC compatibility\n" 1401 " -n=<name> name of the dll\n" 1402 " --implib generate a def file for an import library\n" 1403 " --no-private-warnings suppress warnings about symbols that should be -private\n" 1404 " -a=<arch> set architecture to <arch> (i386, x86_64, arm)\n" 1405 " --with-tracing generate wine-like \"+relay\" trace trampolines (needs -s)\n"); 1406 } 1407 1408 int main(int argc, char *argv[]) 1409 { 1410 size_t nFileSize; 1411 char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL; 1412 const char* pszVersionOption = "--version=0x"; 1413 char achDllName[40]; 1414 FILE *file; 1415 unsigned cExports = 0, i; 1416 EXPORT *pexports; 1417 1418 if (argc < 2) 1419 { 1420 usage(); 1421 return -1; 1422 } 1423 1424 /* Read options */ 1425 for (i = 1; i < (unsigned)argc && *argv[i] == '-'; i++) 1426 { 1427 if ((strcasecmp(argv[i], "--help") == 0) || 1428 (strcasecmp(argv[i], "-h") == 0)) 1429 { 1430 usage(); 1431 return 0; 1432 } 1433 else if (argv[i][1] == 'd' && argv[i][2] == '=') 1434 { 1435 pszDefFileName = argv[i] + 3; 1436 } 1437 else if (argv[i][1] == 'l' && argv[i][2] == '=') 1438 { 1439 pszLibStubName = argv[i] + 3; 1440 } 1441 else if (argv[i][1] == 's' && argv[i][2] == '=') 1442 { 1443 pszStubFileName = argv[i] + 3; 1444 } 1445 else if (argv[i][1] == 'n' && argv[i][2] == '=') 1446 { 1447 pszDllName = argv[i] + 3; 1448 } 1449 else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0) 1450 { 1451 guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16); 1452 } 1453 else if (strcasecmp(argv[i], "--implib") == 0) 1454 { 1455 gbImportLib = 1; 1456 } 1457 else if (strcasecmp(argv[i], "--ms") == 0) 1458 { 1459 gbMSComp = 1; 1460 } 1461 else if (strcasecmp(argv[i], "--no-private-warnings") == 0) 1462 { 1463 gbNotPrivateNoWarn = 1; 1464 } 1465 else if (strcasecmp(argv[i], "--with-tracing") == 0) 1466 { 1467 if (!pszStubFileName) 1468 { 1469 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n"); 1470 return -1; 1471 } 1472 gbTracing = 1; 1473 } 1474 else if (argv[i][1] == 'a' && argv[i][2] == '=') 1475 { 1476 pszArchString = argv[i] + 3; 1477 } 1478 else 1479 { 1480 fprintf(stderr, "Unrecognized option: %s\n", argv[i]); 1481 return -1; 1482 } 1483 } 1484 1485 if (strcasecmp(pszArchString, "i386") == 0) 1486 { 1487 giArch = ARCH_X86; 1488 gpszUnderscore = "_"; 1489 } 1490 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64; 1491 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64; 1492 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM; 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