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