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