1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <ctype.h> 4 #include <string.h> 5 6 #ifdef _MSC_VER 7 #define strcasecmp(_String1, _String2) _stricmp(_String1, _String2) 8 #define strncasecmp(_String1, _String2, _MaxCount) _strnicmp(_String1, _String2, _MaxCount) 9 #endif 10 11 #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) 12 13 typedef struct _STRING 14 { 15 const char *buf; 16 int len; 17 } STRING, *PSTRING; 18 19 typedef struct 20 { 21 STRING strName; 22 STRING strTarget; 23 int nCallingConvention; 24 int nOrdinal; 25 int nStackBytes; 26 int nArgCount; 27 int anArgs[30]; 28 unsigned int uFlags; 29 int nNumber; 30 } EXPORT; 31 32 enum _ARCH 33 { 34 ARCH_X86, 35 ARCH_AMD64, 36 ARCH_IA64, 37 ARCH_ARM, 38 ARCH_PPC 39 }; 40 41 typedef int (*PFNOUTLINE)(FILE *, EXPORT *); 42 int gbMSComp = 0; 43 int gbImportLib = 0; 44 int gbNotPrivateNoWarn = 0; 45 int gbTracing = 0; 46 int giArch = ARCH_X86; 47 char *pszArchString = "i386"; 48 char *pszArchString2; 49 char *pszSourceFileName = NULL; 50 char *pszDllName = NULL; 51 char *gpszUnderscore = ""; 52 int gbDebug; 53 unsigned guOsVersion = 0x502; 54 #define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__)) 55 56 enum 57 { 58 FL_PRIVATE = 1, 59 FL_STUB = 2, 60 FL_NONAME = 4, 61 FL_ORDINAL = 8, 62 FL_NORELAY = 16, 63 FL_RET64 = 32, 64 FL_REGISTER = 64, 65 }; 66 67 enum 68 { 69 CC_STDCALL, 70 CC_CDECL, 71 CC_FASTCALL, 72 CC_THISCALL, 73 CC_EXTERN, 74 CC_STUB, 75 }; 76 77 enum 78 { 79 ARG_LONG, 80 ARG_PTR, 81 ARG_STR, 82 ARG_WSTR, 83 ARG_DBL, 84 ARG_INT64, 85 ARG_INT128, 86 ARG_FLOAT 87 }; 88 89 const char* astrCallingConventions[] = 90 { 91 "STDCALL", 92 "CDECL", 93 "FASTCALL", 94 "THISCALL", 95 "EXTERN" 96 }; 97 98 static const char* astrShouldBePrivate[] = 99 { 100 "DllCanUnloadNow", 101 "DllGetClassObject", 102 "DllGetClassFactoryFromClassString", 103 "DllGetDocumentation", 104 "DllInitialize", 105 "DllInstall", 106 "DllRegisterServer", 107 "DllRegisterServerEx", 108 "DllRegisterServerExW", 109 "DllUnload", 110 "DllUnregisterServer", 111 "RasCustomDeleteEntryNotify", 112 "RasCustomDial", 113 "RasCustomDialDlg", 114 "RasCustomEntryDlg", 115 }; 116 117 static 118 int 119 IsSeparator(char chr) 120 { 121 return ((chr <= ',' && chr != '$' && chr != '#') || 122 (chr >= ':' && chr < '?') ); 123 } 124 125 int 126 CompareToken(const char *token, const char *comparand) 127 { 128 while (*comparand) 129 { 130 if (*token != *comparand) return 0; 131 token++; 132 comparand++; 133 } 134 if (IsSeparator(comparand[-1])) return 1; 135 if (!IsSeparator(*token)) return 0; 136 return 1; 137 } 138 139 const char * 140 ScanToken(const char *token, char chr) 141 { 142 while (!IsSeparator(*token)) 143 { 144 if (*token == chr) return token; 145 token++; 146 } 147 return 0; 148 } 149 150 char * 151 NextLine(char *pc) 152 { 153 while (*pc != 0) 154 { 155 if (pc[0] == '\n' && pc[1] == '\r') return pc + 2; 156 else if (pc[0] == '\n') return pc + 1; 157 pc++; 158 } 159 return pc; 160 } 161 162 int 163 TokenLength(char *pc) 164 { 165 int length = 0; 166 167 while (!IsSeparator(*pc++)) length++; 168 169 return length; 170 } 171 172 char * 173 NextToken(char *pc) 174 { 175 /* Skip token */ 176 while (!IsSeparator(*pc)) pc++; 177 178 /* Skip white spaces */ 179 while (*pc == ' ' || *pc == '\t') pc++; 180 181 /* Check for end of line */ 182 if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0; 183 184 /* Check for comment */ 185 if (*pc == '#' || *pc == ';') return 0; 186 187 return pc; 188 } 189 190 void 191 OutputHeader_stub(FILE *file) 192 { 193 fprintf(file, "/* This file is autogenerated, do not edit. */\n\n" 194 "#include <stubs.h>\n"); 195 196 if (gbTracing) 197 { 198 fprintf(file, "#include <wine/debug.h>\n"); 199 fprintf(file, "#include <inttypes.h>\n"); 200 fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n"); 201 } 202 203 fprintf(file, "\n"); 204 } 205 206 int 207 OutputLine_stub(FILE *file, EXPORT *pexp) 208 { 209 int i; 210 int bRelay = 0; 211 int bInPrototype = 0; 212 213 if (pexp->nCallingConvention != CC_STUB && 214 (pexp->uFlags & FL_STUB) == 0) 215 { 216 /* Only relay trace stdcall C functions */ 217 if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL) 218 || (pexp->uFlags & FL_NORELAY) 219 || (pexp->strName.buf[0] == '?')) 220 { 221 return 0; 222 } 223 bRelay = 1; 224 } 225 226 /* Declare the "real" function */ 227 if (bRelay) 228 { 229 fprintf(file, "extern "); 230 bInPrototype = 1; 231 } 232 233 do 234 { 235 if (pexp->uFlags & FL_REGISTER) 236 { 237 /* FIXME: Not sure this is right */ 238 fprintf(file, "void "); 239 } 240 else if (pexp->uFlags & FL_RET64) 241 { 242 fprintf(file, "__int64 "); 243 } 244 else 245 { 246 fprintf(file, "int "); 247 } 248 249 if ((giArch == ARCH_X86) && 250 pexp->nCallingConvention == CC_STDCALL) 251 { 252 fprintf(file, "__stdcall "); 253 } 254 255 /* Check for C++ */ 256 if (pexp->strName.buf[0] == '?') 257 { 258 fprintf(file, "stub_function%d(", pexp->nNumber); 259 } 260 else 261 { 262 if (!bRelay || bInPrototype) 263 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf); 264 else 265 fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf); 266 } 267 268 for (i = 0; i < pexp->nArgCount; i++) 269 { 270 if (i != 0) fprintf(file, ", "); 271 switch (pexp->anArgs[i]) 272 { 273 case ARG_LONG: fprintf(file, "long"); break; 274 case ARG_PTR: fprintf(file, "void*"); break; 275 case ARG_STR: fprintf(file, "char*"); break; 276 case ARG_WSTR: fprintf(file, "wchar_t*"); break; 277 case ARG_DBL: fprintf(file, "double"); break; 278 case ARG_INT64 : fprintf(file, "__int64"); break; 279 /* __int128 is not supported on x86, and int128 in spec files most often represents a GUID */ 280 case ARG_INT128 : fprintf(file, "GUID"); break; 281 case ARG_FLOAT: fprintf(file, "float"); break; 282 } 283 fprintf(file, " a%d", i); 284 } 285 286 if (bInPrototype) 287 { 288 fprintf(file, ");\n\n"); 289 } 290 } while (bInPrototype--); 291 292 if (!bRelay) 293 { 294 fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(", 295 pexp->strName.len, pexp->strName.buf); 296 } 297 else 298 { 299 fprintf(file, ")\n{\n"); 300 if (pexp->uFlags & FL_REGISTER) 301 { 302 /* No return value */ 303 } 304 else if (pexp->uFlags & FL_RET64) 305 { 306 fprintf(file, "\t__int64 retval;\n"); 307 } 308 else 309 { 310 fprintf(file, "\tint retval;\n"); 311 } 312 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(", 313 pszDllName, pexp->strName.len, pexp->strName.buf); 314 } 315 316 for (i = 0; i < pexp->nArgCount; i++) 317 { 318 if (i != 0) fprintf(file, ","); 319 switch (pexp->anArgs[i]) 320 { 321 case ARG_LONG: fprintf(file, "0x%%lx"); break; 322 case ARG_PTR: fprintf(file, "0x%%p"); break; 323 case ARG_STR: fprintf(file, "'%%s'"); break; 324 case ARG_WSTR: fprintf(file, "'%%ws'"); break; 325 case ARG_DBL: fprintf(file, "%%f"); break; 326 case ARG_INT64: fprintf(file, "%%\"PRIx64\""); break; 327 case ARG_INT128: fprintf(file, "'%%s'"); break; 328 case ARG_FLOAT: fprintf(file, "%%f"); break; 329 } 330 } 331 fprintf(file, ")\\n\""); 332 333 for (i = 0; i < pexp->nArgCount; i++) 334 { 335 fprintf(file, ", "); 336 switch (pexp->anArgs[i]) 337 { 338 case ARG_LONG: fprintf(file, "(long)a%d", i); break; 339 case ARG_PTR: fprintf(file, "(void*)a%d", i); break; 340 case ARG_STR: fprintf(file, "(char*)a%d", i); break; 341 case ARG_WSTR: fprintf(file, "(wchar_t*)a%d", i); break; 342 case ARG_DBL: fprintf(file, "(double)a%d", i); break; 343 case ARG_INT64: fprintf(file, "(__int64)a%d", i); break; 344 case ARG_INT128: fprintf(file, "wine_dbgstr_guid(&a%d)", i); break; 345 case ARG_FLOAT: fprintf(file, "(float)a%d", i); break; 346 } 347 } 348 fprintf(file, ");\n"); 349 350 if (pexp->nCallingConvention == CC_STUB) 351 { 352 fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName); 353 } 354 else if (bRelay) 355 { 356 if (pexp->uFlags & FL_REGISTER) 357 { 358 fprintf(file,"\t"); 359 } 360 else 361 { 362 fprintf(file, "\tretval = "); 363 } 364 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf); 365 366 for (i = 0; i < pexp->nArgCount; i++) 367 { 368 if (i != 0) fprintf(file, ", "); 369 fprintf(file, "a%d", i); 370 } 371 fprintf(file, ");\n"); 372 } 373 374 if (!bRelay) 375 fprintf(file, "\treturn 0;\n}\n\n"); 376 else if ((pexp->uFlags & FL_REGISTER) == 0) 377 { 378 if (pexp->uFlags & FL_RET64) 379 { 380 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = %%\"PRIx64\"\\n\", retval);\n", 381 pszDllName, pexp->strName.len, pexp->strName.buf); 382 } 383 else 384 { 385 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n", 386 pszDllName, pexp->strName.len, pexp->strName.buf); 387 } 388 fprintf(file, "\treturn retval;\n}\n\n"); 389 } 390 391 return 1; 392 } 393 394 void 395 OutputHeader_asmstub(FILE *file, char *libname) 396 { 397 fprintf(file, "; File generated automatically, do not edit! \n\n"); 398 399 if (giArch == ARCH_X86) 400 { 401 fprintf(file, ".586\n.model flat\n.code\n"); 402 } 403 else if (giArch == ARCH_AMD64) 404 { 405 fprintf(file, ".code\n"); 406 } 407 else if (giArch == ARCH_ARM) 408 { 409 fprintf(file, " AREA |.text|,ALIGN=2,CODE,READONLY\n\n"); 410 } 411 } 412 413 void 414 Output_stublabel(FILE *fileDest, char* pszSymbolName) 415 { 416 if (giArch == ARCH_ARM) 417 { 418 fprintf(fileDest, 419 "\tEXPORT |%s| [FUNC]\n|%s|\n", 420 pszSymbolName, 421 pszSymbolName); 422 } 423 else 424 { 425 fprintf(fileDest, 426 "PUBLIC %s\n%s: nop\n", 427 pszSymbolName, 428 pszSymbolName); 429 } 430 } 431 432 int 433 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp) 434 { 435 char szNameBuffer[128]; 436 437 /* Handle autoname */ 438 if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@') 439 { 440 sprintf(szNameBuffer, "%sordinal%d\n%sordinal%d: nop\n", 441 gpszUnderscore, pexp->nOrdinal, gpszUnderscore, pexp->nOrdinal); 442 } 443 else if (giArch != ARCH_X86) 444 { 445 sprintf(szNameBuffer, "_stub_%.*s", 446 pexp->strName.len, pexp->strName.buf); 447 } 448 else if (pexp->nCallingConvention == CC_STDCALL) 449 { 450 sprintf(szNameBuffer, "__stub_%.*s@%d", 451 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes); 452 } 453 else if (pexp->nCallingConvention == CC_FASTCALL) 454 { 455 sprintf(szNameBuffer, "@_stub_%.*s@%d", 456 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes); 457 } 458 else if ((pexp->nCallingConvention == CC_CDECL) || 459 (pexp->nCallingConvention == CC_THISCALL) || 460 (pexp->nCallingConvention == CC_EXTERN) || 461 (pexp->nCallingConvention == CC_STUB)) 462 { 463 sprintf(szNameBuffer, "__stub_%.*s", 464 pexp->strName.len, pexp->strName.buf); 465 } 466 else 467 { 468 fprintf(stderr, "Invalid calling convention"); 469 return 0; 470 } 471 472 Output_stublabel(fileDest, szNameBuffer); 473 474 return 1; 475 } 476 477 void 478 OutputHeader_def(FILE *file, char *libname) 479 { 480 fprintf(file, 481 "; File generated automatically, do not edit!\n\n" 482 "NAME %s\n\n" 483 "EXPORTS\n", 484 libname); 485 } 486 487 void 488 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco) 489 { 490 const char *pcName = pstr->buf; 491 int nNameLength = pstr->len; 492 const char* pcDot, *pcAt; 493 494 /* Check for non-x86 first */ 495 if (giArch != ARCH_X86) 496 { 497 /* Does the string already have stdcall decoration? */ 498 pcAt = ScanToken(pcName, '@'); 499 if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_')) 500 { 501 /* Skip leading underscore and remove trailing decoration */ 502 pcName++; 503 nNameLength = pcAt - pcName; 504 } 505 506 /* Print the undecorated function name */ 507 fprintf(fileDest, "%.*s", nNameLength, pcName); 508 } 509 else if (fDeco && 510 ((pexp->nCallingConvention == CC_STDCALL) || 511 (pexp->nCallingConvention == CC_FASTCALL))) 512 { 513 /* Scan for a dll forwarding dot */ 514 pcDot = ScanToken(pcName, '.'); 515 if (pcDot) 516 { 517 /* First print the dll name, followed by a dot */ 518 nNameLength = pcDot - pcName; 519 fprintf(fileDest, "%.*s.", nNameLength, pcName); 520 521 /* Now the actual function name */ 522 pcName = pcDot + 1; 523 nNameLength = pexp->strTarget.len - nNameLength - 1; 524 } 525 526 /* Does the string already have decoration? */ 527 pcAt = ScanToken(pcName, '@'); 528 if (pcAt && (pcAt < (pcName + nNameLength))) 529 { 530 /* On GCC, we need to remove the leading stdcall underscore */ 531 if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL)) 532 { 533 pcName++; 534 nNameLength--; 535 } 536 537 /* Print the already decorated function name */ 538 fprintf(fileDest, "%.*s", nNameLength, pcName); 539 } 540 else 541 { 542 /* Print the prefix, but skip it for (GCC && stdcall) */ 543 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL)) 544 { 545 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_'); 546 } 547 548 /* Print the name with trailing decoration */ 549 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes); 550 } 551 } 552 else 553 { 554 /* Print the undecorated function name */ 555 fprintf(fileDest, "%.*s", nNameLength, pcName); 556 } 557 } 558 559 void 560 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp) 561 { 562 PrintName(fileDest, pexp, &pexp->strName, 0); 563 564 if (gbImportLib) 565 { 566 /* Redirect to a stub function, to get the right decoration in the lib */ 567 fprintf(fileDest, "=_stub_%.*s", pexp->strName.len, pexp->strName.buf); 568 } 569 else if (pexp->strTarget.buf) 570 { 571 if (pexp->strName.buf[0] == '?') 572 { 573 //fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n", 574 // pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf); 575 } 576 else 577 { 578 fprintf(fileDest, "="); 579 580 /* If the original name was decorated, use decoration in the forwarder as well */ 581 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') && 582 !ScanToken(pexp->strTarget.buf, '@') && 583 ((pexp->nCallingConvention == CC_STDCALL) || 584 (pexp->nCallingConvention == CC_FASTCALL)) ) 585 { 586 PrintName(fileDest, pexp, &pexp->strTarget, 1); 587 } 588 else 589 { 590 /* Write the undecorated redirection name */ 591 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf); 592 } 593 } 594 } 595 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 596 (pexp->strName.buf[0] == '?')) 597 { 598 /* C++ stubs are forwarded to C stubs */ 599 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 600 } 601 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) && 602 (pexp->strName.buf[0] != '?')) 603 { 604 /* Redirect it to the relay-tracing trampoline */ 605 fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 606 } 607 } 608 609 void 610 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp) 611 { 612 int bTracing = 0; 613 /* Print the function name, with decoration for export libs */ 614 PrintName(fileDest, pexp, &pexp->strName, gbImportLib); 615 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf); 616 617 /* Check if this is a forwarded export */ 618 if (pexp->strTarget.buf) 619 { 620 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.'); 621 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf); 622 623 /* print the target name, don't decorate if it is external */ 624 fprintf(fileDest, "="); 625 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal); 626 } 627 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 628 (pexp->strName.buf[0] == '?')) 629 { 630 /* C++ stubs are forwarded to C stubs */ 631 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 632 } 633 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && 634 (pexp->nCallingConvention == CC_STDCALL) && 635 (pexp->strName.buf[0] != '?')) 636 { 637 /* Redirect it to the relay-tracing trampoline */ 638 char buf[256]; 639 STRING strTarget; 640 fprintf(fileDest, "="); 641 sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 642 strTarget.buf = buf; 643 strTarget.len = pexp->strName.len + 12; 644 PrintName(fileDest, pexp, &strTarget, 1); 645 bTracing = 1; 646 } 647 648 /* Special handling for stdcall and fastcall */ 649 if ((giArch == ARCH_X86) && 650 ((pexp->nCallingConvention == CC_STDCALL) || 651 (pexp->nCallingConvention == CC_FASTCALL))) 652 { 653 /* Is this the import lib? */ 654 if (gbImportLib) 655 { 656 /* Is the name in the spec file decorated? */ 657 const char* pcDeco = ScanToken(pexp->strName.buf, '@'); 658 if (pcDeco && (pcDeco < pexp->strName.buf + pexp->strName.len)) 659 { 660 /* Write the name including the leading @ */ 661 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf); 662 } 663 } 664 else if ((!pexp->strTarget.buf) && !(bTracing)) 665 { 666 /* Write a forwarder to the actual decorated symbol */ 667 fprintf(fileDest, "="); 668 PrintName(fileDest, pexp, &pexp->strName, 1); 669 } 670 } 671 } 672 673 int 674 OutputLine_def(FILE *fileDest, EXPORT *pexp) 675 { 676 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf); 677 fprintf(fileDest, " "); 678 679 if (gbMSComp) 680 OutputLine_def_MS(fileDest, pexp); 681 else 682 OutputLine_def_GCC(fileDest, pexp); 683 684 if (pexp->uFlags & FL_ORDINAL) 685 { 686 fprintf(fileDest, " @%d", pexp->nOrdinal); 687 } 688 689 if (pexp->uFlags & FL_NONAME) 690 { 691 fprintf(fileDest, " NONAME"); 692 } 693 694 /* Either PRIVATE or DATA */ 695 if (pexp->uFlags & FL_PRIVATE) 696 { 697 fprintf(fileDest, " PRIVATE"); 698 } 699 else if (pexp->nCallingConvention == CC_EXTERN) 700 { 701 fprintf(fileDest, " DATA"); 702 } 703 704 fprintf(fileDest, "\n"); 705 706 return 1; 707 } 708 709 int 710 ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine) 711 { 712 char *pc, *pcLine; 713 int nLine; 714 EXPORT exp; 715 int included, version_included; 716 char namebuffer[16]; 717 unsigned int i; 718 719 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart); 720 721 /* Loop all lines */ 722 nLine = 1; 723 exp.nNumber = 0; 724 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++) 725 { 726 pc = pcLine; 727 728 exp.nArgCount = 0; 729 exp.uFlags = 0; 730 exp.nNumber++; 731 732 /* Skip white spaces */ 733 while (*pc == ' ' || *pc == '\t') pc++; 734 735 /* Skip empty lines, stop at EOF */ 736 if (*pc == ';' || *pc <= '#') continue; 737 if (*pc == 0) return 0; 738 739 /* Now we should get either an ordinal or @ */ 740 if (*pc == '@') 741 exp.nOrdinal = -1; 742 else 743 { 744 exp.nOrdinal = atol(pc); 745 /* The import lib should contain the ordinal only if -ordinal was specified */ 746 if (!gbImportLib) 747 exp.uFlags |= FL_ORDINAL; 748 } 749 750 /* Go to next token (type) */ 751 if (!(pc = NextToken(pc))) 752 { 753 fprintf(stderr, "%s line %d: error: unexpected end of line\n", pszSourceFileName, nLine); 754 return -10; 755 } 756 757 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc); 758 759 /* Now we should get the type */ 760 if (CompareToken(pc, "stdcall")) 761 { 762 exp.nCallingConvention = CC_STDCALL; 763 } 764 else if (CompareToken(pc, "cdecl") || 765 CompareToken(pc, "varargs")) 766 { 767 exp.nCallingConvention = CC_CDECL; 768 } 769 else if (CompareToken(pc, "fastcall")) 770 { 771 exp.nCallingConvention = CC_FASTCALL; 772 } 773 else if (CompareToken(pc, "thiscall")) 774 { 775 exp.nCallingConvention = CC_THISCALL; 776 } 777 else if (CompareToken(pc, "extern")) 778 { 779 exp.nCallingConvention = CC_EXTERN; 780 } 781 else if (CompareToken(pc, "stub")) 782 { 783 exp.nCallingConvention = CC_STUB; 784 } 785 else 786 { 787 fprintf(stderr, "%s line %d: error: expected callconv, got '%.*s' %d\n", 788 pszSourceFileName, nLine, TokenLength(pc), pc, *pc); 789 return -11; 790 } 791 792 /* Go to next token (options or name) */ 793 if (!(pc = NextToken(pc))) 794 { 795 fprintf(stderr, "fail2\n"); 796 return -12; 797 } 798 799 /* Handle options */ 800 included = 1; 801 version_included = 1; 802 while (*pc == '-') 803 { 804 if (CompareToken(pc, "-arch=")) 805 { 806 /* Default to not included */ 807 included = 0; 808 pc += 5; 809 810 /* Look if we are included */ 811 do 812 { 813 pc++; 814 if (CompareToken(pc, pszArchString) || 815 CompareToken(pc, pszArchString2)) 816 { 817 included = 1; 818 } 819 820 /* Skip to next arch or end */ 821 while (*pc > ',') pc++; 822 } while (*pc == ','); 823 } 824 else if (CompareToken(pc, "-i386")) 825 { 826 if (giArch != ARCH_X86) included = 0; 827 } 828 else if (CompareToken(pc, "-version=")) 829 { 830 /* Default to not included */ 831 version_included = 0; 832 pc += 8; 833 834 /* Look if we are included */ 835 do 836 { 837 unsigned version, endversion; 838 839 /* Optionally skip leading '0x' */ 840 pc++; 841 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 842 843 /* Now get the version number */ 844 endversion = version = strtoul(pc, &pc, 16); 845 846 /* Check if it's a range */ 847 if (pc[0] == '+') 848 { 849 endversion = 0xFFF; 850 pc++; 851 } 852 else if (pc[0] == '-') 853 { 854 /* Optionally skip leading '0x' */ 855 pc++; 856 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 857 endversion = strtoul(pc, &pc, 16); 858 } 859 860 /* Check for degenerate range */ 861 if (version > endversion) 862 { 863 fprintf(stderr, "%s line %d: error: invalid version rangen\n", pszSourceFileName, nLine); 864 return -1; 865 } 866 867 /* Now compare the range with our version */ 868 if ((guOsVersion >= version) && 869 (guOsVersion <= endversion)) 870 { 871 version_included = 1; 872 } 873 874 /* Skip to next arch or end */ 875 while (*pc > ',') pc++; 876 877 } while (*pc == ','); 878 } 879 else if (CompareToken(pc, "-private")) 880 { 881 exp.uFlags |= FL_PRIVATE; 882 } 883 else if (CompareToken(pc, "-noname")) 884 { 885 exp.uFlags |= FL_ORDINAL | FL_NONAME; 886 } 887 else if (CompareToken(pc, "-ordinal")) 888 { 889 exp.uFlags |= FL_ORDINAL; 890 /* GCC doesn't automatically import by ordinal if an ordinal 891 * is found in the def file. Force it. */ 892 if (gbImportLib && !gbMSComp) 893 exp.uFlags |= FL_NONAME; 894 } 895 else if (CompareToken(pc, "-stub")) 896 { 897 exp.uFlags |= FL_STUB; 898 } 899 else if (CompareToken(pc, "-norelay")) 900 { 901 exp.uFlags |= FL_NORELAY; 902 } 903 else if (CompareToken(pc, "-ret64")) 904 { 905 exp.uFlags |= FL_RET64; 906 } 907 else if (CompareToken(pc, "-register")) 908 { 909 exp.uFlags |= FL_REGISTER; 910 } 911 else 912 { 913 fprintf(stderr, "info: ignored option: '%.*s'\n", 914 TokenLength(pc), pc); 915 } 916 917 /* Go to next token */ 918 pc = NextToken(pc); 919 } 920 921 //fprintf(stderr, "info: Name:'%.10s'\n", pc); 922 923 /* If arch didn't match ours, skip this entry */ 924 if (!included || !version_included) continue; 925 926 /* Get name */ 927 exp.strName.buf = pc; 928 exp.strName.len = TokenLength(pc); 929 DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf); 930 931 /* Check for autoname */ 932 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@')) 933 { 934 sprintf(namebuffer, "ordinal%d", exp.nOrdinal); 935 exp.strName.len = strlen(namebuffer); 936 exp.strName.buf = namebuffer; 937 exp.uFlags |= FL_ORDINAL | FL_NONAME; 938 } 939 940 /* Handle parameters */ 941 exp.nStackBytes = 0; 942 if (exp.nCallingConvention != CC_EXTERN && 943 exp.nCallingConvention != CC_STUB) 944 { 945 /* Go to next token */ 946 if (!(pc = NextToken(pc))) 947 { 948 fprintf(stderr, "%s line %d: error: expected token\n", pszSourceFileName, nLine); 949 return -13; 950 } 951 952 /* Verify syntax */ 953 if (*pc++ != '(') 954 { 955 fprintf(stderr, "%s line %d: error: expected '('\n", pszSourceFileName, nLine); 956 return -14; 957 } 958 959 /* Skip whitespaces */ 960 while (*pc == ' ' || *pc == '\t') pc++; 961 962 exp.nStackBytes = 0; 963 while (*pc >= '0') 964 { 965 if (CompareToken(pc, "long")) 966 { 967 exp.nStackBytes += 4; 968 exp.anArgs[exp.nArgCount] = ARG_LONG; 969 } 970 else if (CompareToken(pc, "double")) 971 { 972 exp.nStackBytes += 8; 973 exp.anArgs[exp.nArgCount] = ARG_DBL; 974 } 975 else if (CompareToken(pc, "ptr")) 976 { 977 exp.nStackBytes += 4; // sizeof(void*) on x86 978 exp.anArgs[exp.nArgCount] = ARG_PTR; 979 } 980 else if (CompareToken(pc, "str")) 981 { 982 exp.nStackBytes += 4; // sizeof(void*) on x86 983 exp.anArgs[exp.nArgCount] = ARG_STR; 984 } 985 else if (CompareToken(pc, "wstr")) 986 { 987 exp.nStackBytes += 4; // sizeof(void*) on x86 988 exp.anArgs[exp.nArgCount] = ARG_WSTR; 989 } 990 else if (CompareToken(pc, "int64")) 991 { 992 exp.nStackBytes += 8; 993 exp.anArgs[exp.nArgCount] = ARG_INT64; 994 } 995 else if (CompareToken(pc, "int128")) 996 { 997 exp.nStackBytes += 16; 998 exp.anArgs[exp.nArgCount] = ARG_INT128; 999 } 1000 else if (CompareToken(pc, "float")) 1001 { 1002 exp.nStackBytes += 4; 1003 exp.anArgs[exp.nArgCount] = ARG_FLOAT; 1004 } 1005 else 1006 fprintf(stderr, "%s line %d: error: expected type, got: %.10s\n", pszSourceFileName, nLine, pc); 1007 1008 exp.nArgCount++; 1009 1010 /* Go to next parameter */ 1011 if (!(pc = NextToken(pc))) 1012 { 1013 fprintf(stderr, "fail5\n"); 1014 return -15; 1015 } 1016 } 1017 1018 /* Check syntax */ 1019 if (*pc++ != ')') 1020 { 1021 fprintf(stderr, "%s line %d: error: expected ')'\n", pszSourceFileName, nLine); 1022 return -16; 1023 } 1024 } 1025 1026 /* Handle special stub cases */ 1027 if (exp.nCallingConvention == CC_STUB) 1028 { 1029 /* Check for c++ mangled name */ 1030 if (pc[0] == '?') 1031 { 1032 //printf("Found c++ mangled name...\n"); 1033 // 1034 } 1035 else 1036 { 1037 /* Check for stdcall name */ 1038 const char *p = ScanToken(pc, '@'); 1039 if (p && (p - pc < exp.strName.len)) 1040 { 1041 int i; 1042 1043 /* Truncate the name to before the @ */ 1044 exp.strName.len = (int)(p - pc); 1045 if (exp.strName.len < 1) 1046 { 1047 fprintf(stderr, "%s line %d: error: unexpected @ found\n", pszSourceFileName, nLine); 1048 return -1; 1049 } 1050 exp.nStackBytes = atoi(p + 1); 1051 exp.nArgCount = exp.nStackBytes / 4; 1052 exp.nCallingConvention = CC_STDCALL; 1053 exp.uFlags |= FL_STUB; 1054 for (i = 0; i < exp.nArgCount; i++) 1055 exp.anArgs[i] = ARG_LONG; 1056 } 1057 } 1058 } 1059 1060 /* Get optional redirection */ 1061 pc = NextToken(pc); 1062 if (pc) 1063 { 1064 exp.strTarget.buf = pc; 1065 exp.strTarget.len = TokenLength(pc); 1066 1067 /* Check syntax (end of line) */ 1068 if (NextToken(pc)) 1069 { 1070 fprintf(stderr, "%s line %d: error: additional tokens after ')'\n", pszSourceFileName, nLine); 1071 return -17; 1072 } 1073 1074 /* Don't relay-trace forwarded functions */ 1075 exp.uFlags |= FL_NORELAY; 1076 } 1077 else 1078 { 1079 exp.strTarget.buf = NULL; 1080 exp.strTarget.len = 0; 1081 } 1082 1083 /* Check for no-name without ordinal */ 1084 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1)) 1085 { 1086 fprintf(stderr, "%s line %d: error: ordinal export without ordinal!\n", pszSourceFileName, nLine); 1087 return -1; 1088 } 1089 1090 if (!gbMSComp && !gbNotPrivateNoWarn && gbImportLib && !(exp.uFlags & FL_PRIVATE)) 1091 { 1092 for (i = 0; i < ARRAYSIZE(astrShouldBePrivate); i++) 1093 { 1094 if (strlen(astrShouldBePrivate[i]) == exp.strName.len && 1095 strncmp(exp.strName.buf, astrShouldBePrivate[i], exp.strName.len) == 0) 1096 { 1097 fprintf(stderr, "%s line %d: warning: export of '%.*s' should be PRIVATE\n", 1098 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf); 1099 } 1100 } 1101 } 1102 1103 OutputLine(fileDest, &exp); 1104 gbDebug = 0; 1105 } 1106 1107 return 0; 1108 } 1109 1110 void usage(void) 1111 { 1112 printf("syntax: spec2def [<options> ...] <spec file>\n" 1113 "Possible options:\n" 1114 " -h --help print this help screen\n" 1115 " -l=<file> generate an asm lib stub\n" 1116 " -d=<file> generate a def file\n" 1117 " -s=<file> generate a stub file\n" 1118 " --ms MSVC compatibility\n" 1119 " -n=<name> name of the dll\n" 1120 " --implib generate a def file for an import library\n" 1121 " --no-private-warnings suppress warnings about symbols that should be -private\n" 1122 " -a=<arch> set architecture to <arch> (i386, x86_64, arm)\n" 1123 " --with-tracing generate wine-like \"+relay\" trace trampolines (needs -s)\n"); 1124 } 1125 1126 int main(int argc, char *argv[]) 1127 { 1128 size_t nFileSize; 1129 char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL; 1130 const char* pszVersionOption = "--version=0x"; 1131 char achDllName[40]; 1132 FILE *file; 1133 int result = 0, i; 1134 1135 if (argc < 2) 1136 { 1137 usage(); 1138 return -1; 1139 } 1140 1141 /* Read options */ 1142 for (i = 1; i < argc && *argv[i] == '-'; i++) 1143 { 1144 if ((strcasecmp(argv[i], "--help") == 0) || 1145 (strcasecmp(argv[i], "-h") == 0)) 1146 { 1147 usage(); 1148 return 0; 1149 } 1150 else if (argv[i][1] == 'd' && argv[i][2] == '=') 1151 { 1152 pszDefFileName = argv[i] + 3; 1153 } 1154 else if (argv[i][1] == 'l' && argv[i][2] == '=') 1155 { 1156 pszLibStubName = argv[i] + 3; 1157 } 1158 else if (argv[i][1] == 's' && argv[i][2] == '=') 1159 { 1160 pszStubFileName = argv[i] + 3; 1161 } 1162 else if (argv[i][1] == 'n' && argv[i][2] == '=') 1163 { 1164 pszDllName = argv[i] + 3; 1165 } 1166 else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0) 1167 { 1168 guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16); 1169 } 1170 else if (strcasecmp(argv[i], "--implib") == 0) 1171 { 1172 gbImportLib = 1; 1173 } 1174 else if (strcasecmp(argv[i], "--ms") == 0) 1175 { 1176 gbMSComp = 1; 1177 } 1178 else if (strcasecmp(argv[i], "--no-private-warnings") == 0) 1179 { 1180 gbNotPrivateNoWarn = 1; 1181 } 1182 else if (strcasecmp(argv[i], "--with-tracing") == 0) 1183 { 1184 if (!pszStubFileName) 1185 { 1186 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n"); 1187 return -1; 1188 } 1189 gbTracing = 1; 1190 } 1191 else if (argv[i][1] == 'a' && argv[i][2] == '=') 1192 { 1193 pszArchString = argv[i] + 3; 1194 } 1195 else 1196 { 1197 fprintf(stderr, "Unrecognized option: %s\n", argv[i]); 1198 return -1; 1199 } 1200 } 1201 1202 if (strcasecmp(pszArchString, "i386") == 0) 1203 { 1204 giArch = ARCH_X86; 1205 gpszUnderscore = "_"; 1206 } 1207 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64; 1208 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64; 1209 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM; 1210 else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC; 1211 1212 if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64)) 1213 { 1214 pszArchString2 = "win64"; 1215 } 1216 else 1217 pszArchString2 = "win32"; 1218 1219 /* Set a default dll name */ 1220 if (!pszDllName) 1221 { 1222 char *p1, *p2; 1223 size_t len; 1224 1225 p1 = strrchr(argv[i], '\\'); 1226 if (!p1) p1 = strrchr(argv[i], '/'); 1227 p2 = p1 = p1 ? p1 + 1 : argv[i]; 1228 1229 /* walk up to '.' */ 1230 while (*p2 != '.' && *p2 != 0) p2++; 1231 len = p2 - p1; 1232 if (len >= sizeof(achDllName) - 5) 1233 { 1234 fprintf(stderr, "name too long: %s\n", p1); 1235 return -2; 1236 } 1237 1238 strncpy(achDllName, p1, len); 1239 strncpy(achDllName + len, ".dll", sizeof(achDllName) - len); 1240 pszDllName = achDllName; 1241 } 1242 1243 /* Open input file */ 1244 pszSourceFileName = argv[i]; 1245 file = fopen(pszSourceFileName, "r"); 1246 if (!file) 1247 { 1248 fprintf(stderr, "error: could not open file %s\n", pszSourceFileName); 1249 return -3; 1250 } 1251 1252 /* Get file size */ 1253 fseek(file, 0, SEEK_END); 1254 nFileSize = ftell(file); 1255 rewind(file); 1256 1257 /* Allocate memory buffer */ 1258 pszSource = malloc(nFileSize + 1); 1259 if (!pszSource) 1260 { 1261 fclose(file); 1262 return -4; 1263 } 1264 1265 /* Load input file into memory */ 1266 nFileSize = fread(pszSource, 1, nFileSize, file); 1267 fclose(file); 1268 1269 /* Zero terminate the source */ 1270 pszSource[nFileSize] = '\0'; 1271 1272 if (pszDefFileName) 1273 { 1274 /* Open output file */ 1275 file = fopen(pszDefFileName, "w"); 1276 if (!file) 1277 { 1278 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1279 return -5; 1280 } 1281 1282 OutputHeader_def(file, pszDllName); 1283 result = ParseFile(pszSource, file, OutputLine_def); 1284 fclose(file); 1285 } 1286 1287 if (pszStubFileName) 1288 { 1289 /* Open output file */ 1290 file = fopen(pszStubFileName, "w"); 1291 if (!file) 1292 { 1293 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1294 return -5; 1295 } 1296 1297 OutputHeader_stub(file); 1298 result = ParseFile(pszSource, file, OutputLine_stub); 1299 fclose(file); 1300 } 1301 1302 if (pszLibStubName) 1303 { 1304 /* Open output file */ 1305 file = fopen(pszLibStubName, "w"); 1306 if (!file) 1307 { 1308 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1309 return -5; 1310 } 1311 1312 OutputHeader_asmstub(file, pszDllName); 1313 result = ParseFile(pszSource, file, OutputLine_asmstub); 1314 fprintf(file, "\n END\n"); 1315 fclose(file); 1316 } 1317 1318 return result; 1319 } 1320