[SPEC2DEF]
[reactos.git] / reactos / tools / spec2def / spec2def.c
index 018b72f..5264f41 100644 (file)
@@ -38,6 +38,7 @@ enum _ARCH
 typedef int (*PFNOUTLINE)(FILE *, EXPORT *);
 int gbMSComp = 0;
 int gbImportLib = 0;
+int gbTracing = 0;
 int giArch = ARCH_X86;
 char *pszArchString = "i386";
 char *pszArchString2;
@@ -52,6 +53,9 @@ enum
     FL_STUB = 2,
     FL_NONAME = 4,
     FL_ORDINAL = 8,
+    FL_NORELAY = 16,
+    FL_RET64 = 32,
+    FL_REGISTER = 64,
 };
 
 enum
@@ -161,52 +165,126 @@ void
 OutputHeader_stub(FILE *file)
 {
     fprintf(file, "/* This file is autogenerated, do not edit. */\n\n"
-            "#include <stubs.h>\n\n");
+            "#include <stubs.h>\n");
+
+    if (gbTracing)
+    {
+        fprintf(file, "#include <wine/debug.h>\n");
+        fprintf(file, "#include <inttypes.h>\n");
+        fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n");
+    }
+
+    fprintf(file, "\n");
 }
 
 int
 OutputLine_stub(FILE *file, EXPORT *pexp)
 {
     int i;
+    int bRelay = 0;
+    int bInPrototype = 0;
 
     if (pexp->nCallingConvention != CC_STUB &&
-        (pexp->uFlags & FL_STUB) == 0) return 0;
-
-    fprintf(file, "int ");
-    if ((giArch == ARCH_X86) &&
-        pexp->nCallingConvention == CC_STDCALL)
+        (pexp->uFlags & FL_STUB) == 0)
     {
-        fprintf(file, "__stdcall ");
+        /* Only relay trace stdcall C functions */
+        if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL)
+                || (pexp->uFlags & FL_NORELAY)
+                || (pexp->strName.buf[0] == '?'))
+        {
+            return 0;
+        }
+        bRelay = 1;
     }
 
-    /* Check for C++ */
-    if (pexp->strName.buf[0] == '?')
+    /* Declare the "real" function */
+    if (bRelay)
     {
-        fprintf(file, "stub_function%d(", pexp->nNumber);
+        fprintf(file, "extern ");
+        bInPrototype = 1;
     }
-    else
+
+    do
     {
-        fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
-    }
+        if (pexp->uFlags & FL_REGISTER)
+        {
+            /* FIXME: Not sure this is right */
+            fprintf(file, "void ");
+        }
+        else if (pexp->uFlags & FL_RET64)
+        {
+            fprintf(file, "__int64 ");
+        }
+        else
+        {
+            fprintf(file, "int ");
+        }
 
-    for (i = 0; i < pexp->nArgCount; i++)
+        if ((giArch == ARCH_X86) &&
+            pexp->nCallingConvention == CC_STDCALL)
+        {
+            fprintf(file, "__stdcall ");
+        }
+
+        /* Check for C++ */
+        if (pexp->strName.buf[0] == '?')
+        {
+            fprintf(file, "stub_function%d(", pexp->nNumber);
+        }
+        else
+        {
+            if (!bRelay || bInPrototype)
+                fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
+            else
+                fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf);
+        }
+
+        for (i = 0; i < pexp->nArgCount; i++)
+        {
+            if (i != 0) fprintf(file, ", ");
+            switch (pexp->anArgs[i])
+            {
+                case ARG_LONG: fprintf(file, "long"); break;
+                case ARG_PTR:  fprintf(file, "void*"); break;
+                case ARG_STR:  fprintf(file, "char*"); break;
+                case ARG_WSTR: fprintf(file, "wchar_t*"); break;
+                case ARG_DBL:  fprintf(file, "double"); break;
+                case ARG_INT64 :  fprintf(file, "__int64"); break;
+                case ARG_INT128 :  fprintf(file, "__int128"); break;
+                case ARG_FLOAT: fprintf(file, "float"); break;
+            }
+            fprintf(file, " a%d", i);
+        }
+
+        if (bInPrototype)
+        {
+            fprintf(file, ");\n\n");
+        }
+    } while (bInPrototype--);
+
+    if (!bRelay)
     {
-        if (i != 0) fprintf(file, ", ");
-        switch (pexp->anArgs[i])
+        fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(",
+                pexp->strName.len, pexp->strName.buf);
+    }
+    else
+    {
+        fprintf(file, ")\n{\n");
+        if (pexp->uFlags & FL_REGISTER)
+        {
+            /* No return value */
+        }
+        else if (pexp->uFlags & FL_RET64)
         {
-            case ARG_LONG: fprintf(file, "long"); break;
-            case ARG_PTR:  fprintf(file, "void*"); break;
-            case ARG_STR:  fprintf(file, "char*"); break;
-            case ARG_WSTR: fprintf(file, "wchar_t*"); break;
-            case ARG_DBL:
-            case ARG_INT64 :  fprintf(file, "__int64"); break;
-            case ARG_INT128 :  fprintf(file, "__int128"); break;
-            case ARG_FLOAT: fprintf(file, "float"); break;
+            fprintf(file, "\t__int64 retval;\n");
         }
-        fprintf(file, " a%d", i);
+        else
+        {
+            fprintf(file, "\tint retval;\n");
+        }
+        fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(",
+                        pszDllName, pexp->strName.len, pexp->strName.buf);
     }
-    fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(",
-            pexp->strName.len, pexp->strName.buf);
 
     for (i = 0; i < pexp->nArgCount; i++)
     {
@@ -246,8 +324,42 @@ OutputLine_stub(FILE *file, EXPORT *pexp)
     {
         fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName);
     }
+    else if (bRelay)
+    {
+        if (pexp->uFlags & FL_REGISTER)
+        {
+            fprintf(file,"\t");
+        }
+        else
+        {
+            fprintf(file, "\tretval = ");
+        }
+        fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
 
-    fprintf(file, "\treturn 0;\n}\n\n");
+        for (i = 0; i < pexp->nArgCount; i++)
+        {
+            if (i != 0) fprintf(file, ", ");
+            fprintf(file, "a%d", i);
+        }
+        fprintf(file, ");\n");
+    }
+
+    if (!bRelay)
+        fprintf(file, "\treturn 0;\n}\n\n");
+    else if ((pexp->uFlags & FL_REGISTER) == 0)
+    {
+        if (pexp->uFlags & FL_RET64)
+        {
+            fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = %%\"PRIx64\"\\n\", retval);\n",
+                pszDllName, pexp->strName.len, pexp->strName.buf);
+        }
+        else
+        {
+            fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n",
+                pszDllName, pexp->strName.len, pexp->strName.buf);
+        }
+        fprintf(file, "\treturn retval;\n}\n\n");
+    }
 
     return 1;
 }
@@ -258,52 +370,81 @@ OutputHeader_asmstub(FILE *file, char *libname)
     fprintf(file, "; File generated automatically, do not edit! \n\n");
 
     if (giArch == ARCH_X86)
-        fprintf(file, ".586\n.model flat\n");
+    {
+        fprintf(file, ".586\n.model flat\n.code\n");
+    }
+    else if (giArch == ARCH_AMD64)
+    {
+        fprintf(file, ".code\n");
+    }
+    else if (giArch == ARCH_ARM)
+    {
+        fprintf(file,
+                "    AREA |.text|,ALIGN=2,CODE,READONLY\n\n");
+    }
+}
 
-    fprintf(file, ".code\n");
+void
+Output_stublabel(FILE *fileDest, char* pszSymbolName)
+{
+    if (giArch == ARCH_ARM)
+    {
+        fprintf(fileDest,
+                "      EXPORT %s [FUNC]\n%s\n",
+                pszSymbolName,
+                pszSymbolName);
+    }
+    else
+    {
+        fprintf(fileDest,
+                "PUBLIC %s\n%s: nop\n",
+                pszSymbolName,
+                pszSymbolName);
+    }
 }
 
 int
 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp)
 {
+    char szNameBuffer[128];
+
     /* Handle autoname */
     if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@')
     {
-        fprintf(fileDest, "PUBLIC %sordinal%d\n%sordinal%d: nop\n",
+        sprintf(szNameBuffer, "%sordinal%d\n%sordinal%d: nop\n",
                 gpszUnderscore, pexp->nOrdinal, gpszUnderscore, pexp->nOrdinal);
     }
     else if (giArch != ARCH_X86)
     {
-        fprintf(fileDest, "PUBLIC _stub_%.*s\n_stub_%.*s: nop\n",
-                pexp->strName.len, pexp->strName.buf,
+        sprintf(szNameBuffer, "_stub_%.*s",
                 pexp->strName.len, pexp->strName.buf);
     }
     else if (pexp->nCallingConvention == CC_STDCALL)
     {
-        fprintf(fileDest, "PUBLIC __stub_%.*s@%d\n__stub_%.*s@%d: nop\n",
-                pexp->strName.len, pexp->strName.buf, pexp->nStackBytes,
+        sprintf(szNameBuffer, "__stub_%.*s@%d",
                 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
     }
     else if (pexp->nCallingConvention == CC_FASTCALL)
     {
-        fprintf(fileDest, "PUBLIC @_stub_%.*s@%d\n@_stub_%.*s@%d: nop\n",
-                pexp->strName.len, pexp->strName.buf, pexp->nStackBytes,
+        sprintf(szNameBuffer, "@_stub_%.*s@%d",
                 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
     }
-    else if (pexp->nCallingConvention == CC_CDECL ||
-             pexp->nCallingConvention == CC_STUB)
+    else if ((pexp->nCallingConvention == CC_CDECL) ||
+             (pexp->nCallingConvention == CC_THISCALL) ||
+             (pexp->nCallingConvention == CC_EXTERN) ||
+             (pexp->nCallingConvention == CC_STUB))
     {
-        fprintf(fileDest, "PUBLIC __stub_%.*s\n__stub_%.*s: nop\n",
-                pexp->strName.len, pexp->strName.buf,
+        sprintf(szNameBuffer, "__stub_%.*s",
                 pexp->strName.len, pexp->strName.buf);
     }
-    else if (pexp->nCallingConvention == CC_EXTERN)
+    else
     {
-        fprintf(fileDest, "PUBLIC __stub_%.*s\n__stub_%.*s:\n",
-                pexp->strName.len, pexp->strName.buf,
-                pexp->strName.len, pexp->strName.buf);
+        fprintf(stderr, "Invalid calling convention");
+        return 0;
     }
 
+    Output_stublabel(fileDest, szNameBuffer);
+
     return 1;
 }
 
@@ -324,7 +465,22 @@ PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
     int nNameLength = pstr->len;
     const char* pcDot, *pcAt;
 
-    if ((giArch == ARCH_X86) && fDeco &&
+    /* Check for non-x86 first */
+    if (giArch != ARCH_X86)
+    {
+        /* Does the string already have stdcall decoration? */
+        pcAt = ScanToken(pcName, '@');
+        if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_'))
+        {
+            /* Skip leading underscore and remove trailing decoration */
+            pcName++;
+            nNameLength = pcAt - pcName;
+        }
+
+        /* Print the undecorated function name */
+        fprintf(fileDest, "%.*s", nNameLength, pcName);
+    }
+    else if (fDeco &&
         ((pexp->nCallingConvention == CC_STDCALL) ||
          (pexp->nCallingConvention == CC_FASTCALL)))
     {
@@ -416,11 +572,18 @@ OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
         /* C++ stubs are forwarded to C stubs */
         fprintf(fileDest, "=stub_function%d", pexp->nNumber);
     }
+    else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) &&
+            (pexp->strName.buf[0] != '?'))
+    {
+        /* Redirect it to the relay-tracing trampoline */
+        fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
+    }
 }
 
 void
 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
 {
+    int bTracing = 0;
     /* Print the function name, with decoration for export libs */
     PrintName(fileDest, pexp, &pexp->strName, gbImportLib);
     DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf);
@@ -441,6 +604,19 @@ OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
         /* C++ stubs are forwarded to C stubs */
         fprintf(fileDest, "=stub_function%d", pexp->nNumber);
     }
+    else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) &&
+            (pexp->strName.buf[0] != '?'))
+    {
+        /* Redirect it to the relay-tracing trampoline */
+        char buf[256];
+        STRING strTarget;
+        fprintf(fileDest, "=");
+        sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
+        strTarget.buf = buf;
+        strTarget.len = pexp->strName.len + 12;
+        PrintName(fileDest, pexp, &strTarget, 1);
+        bTracing = 1;
+    }
 
     /* Special handling for stdcall and fastcall */
     if ((giArch == ARCH_X86) &&
@@ -458,7 +634,7 @@ OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
                 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
             }
         }
-        else if (!pexp->strTarget.buf)
+        else if ((!pexp->strTarget.buf) && !(bTracing))
         {
             /* Write a forwarder to the actual decorated symbol */
             fprintf(fileDest, "=");
@@ -470,6 +646,7 @@ OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
 int
 OutputLine_def(FILE *fileDest, EXPORT *pexp)
 {
+    DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
     fprintf(fileDest, " ");
 
     if (gbMSComp)
@@ -547,7 +724,9 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
         else
         {
             exp.nOrdinal = atol(pc);
-            exp.uFlags |= FL_ORDINAL;
+            /* The import lib should contain the ordinal only if -ordinal was specified */
+            if (!gbImportLib)
+                exp.uFlags |= FL_ORDINAL;
         }
 
         /* Go to next token (type) */
@@ -640,16 +819,26 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
             else if (CompareToken(pc, "-ordinal"))
             {
                 exp.uFlags |= FL_ORDINAL;
+                /* GCC doesn't automatically import by ordinal if an ordinal
+                 * is found in the def file. Force it. */
+                if (gbImportLib && !gbMSComp)
+                    exp.uFlags |= FL_NONAME;
             }
             else if (CompareToken(pc, "-stub"))
             {
                 exp.uFlags |= FL_STUB;
             }
-            else if (CompareToken(pc, "-norelay") ||
-                     CompareToken(pc, "-register") ||
-                     CompareToken(pc, "-ret64"))
+            else if (CompareToken(pc, "-norelay"))
+            {
+                exp.uFlags |= FL_NORELAY;
+            }
+            else if (CompareToken(pc, "-ret64"))
+            {
+                exp.uFlags |= FL_RET64;
+            }
+            else if (CompareToken(pc, "-register"))
             {
-                /* silently ignore these */
+                exp.uFlags |= FL_REGISTER;
             }
             else
             {
@@ -669,6 +858,7 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
         /* Get name */
         exp.strName.buf = pc;
         exp.strName.len = TokenLength(pc);
+        DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
 
         /* Check for autoname */
         if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
@@ -684,7 +874,6 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
         if (exp.nCallingConvention != CC_EXTERN &&
             exp.nCallingConvention != CC_STUB)
         {
-            //fprintf(stderr, "info: options:'%.10s'\n", pc);
             /* Go to next token */
             if (!(pc = NextToken(pc)))
             {
@@ -715,12 +904,20 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
                     exp.nStackBytes += 8;
                     exp.anArgs[exp.nArgCount] = ARG_DBL;
                 }
-                else if (CompareToken(pc, "ptr") ||
-                         CompareToken(pc, "str") ||
-                         CompareToken(pc, "wstr"))
+                else if (CompareToken(pc, "ptr"))
+                {
+                    exp.nStackBytes += 4; // sizeof(void*) on x86
+                    exp.anArgs[exp.nArgCount] = ARG_PTR;
+                }
+                else if (CompareToken(pc, "str"))
+                {
+                    exp.nStackBytes += 4; // sizeof(void*) on x86
+                    exp.anArgs[exp.nArgCount] = ARG_STR;
+                }
+                else if (CompareToken(pc, "wstr"))
                 {
                     exp.nStackBytes += 4; // sizeof(void*) on x86
-                    exp.anArgs[exp.nArgCount] = ARG_PTR; // FIXME: handle strings
+                    exp.anArgs[exp.nArgCount] = ARG_WSTR;
                 }
                 else if (CompareToken(pc, "int64"))
                 {
@@ -805,6 +1002,9 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
                  fprintf(stderr, "error: line %d, additional tokens after ')'\n", nLine);
                  return -17;
             }
+
+            /* Don't relay-trace forwarded functions */
+            exp.uFlags |= FL_NORELAY;
         }
         else
         {
@@ -829,16 +1029,17 @@ ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
 
 void usage(void)
 {
-    printf("syntax: spec2pdef [<options> ...] <spec file>\n"
+    printf("syntax: spec2def [<options> ...] <spec file>\n"
            "Possible options:\n"
-           "  -h --help   prints this screen\n"
-           "  -l=<file>   generates an asm lib stub\n"
-           "  -d=<file>   generates a def file\n"
-           "  -s=<file>   generates a stub file\n"
-           "  --ms        msvc compatibility\n"
-           "  -n=<name>   name of the dll\n"
-           "  --implib    generate a def file for an import library\n"
-           "  -a=<arch>   Set architecture to <arch>. (i386, x86_64, arm)\n");
+           "  -h --help       prints this screen\n"
+           "  -l=<file>       generates an asm lib stub\n"
+           "  -d=<file>       generates a def file\n"
+           "  -s=<file>       generates a stub file\n"
+           "  --ms            msvc compatibility\n"
+           "  -n=<name>       name of the dll\n"
+           "  --implib        generate a def file for an import library\n"
+           "  -a=<arch>       Set architecture to <arch>. (i386, x86_64, arm)\n"
+           "  --with-tracing generates wine-like \"+relay\" trace trampolines. (necessitates -s)\n");
 }
 
 int main(int argc, char *argv[])
@@ -888,6 +1089,15 @@ int main(int argc, char *argv[])
         {
             gbMSComp = 1;
         }
+        else if ((strcasecmp(argv[i], "--with-tracing") == 0))
+        {
+            if (!pszStubFileName)
+            {
+                fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n");
+                return -1;
+            }
+            gbTracing = 1;
+        }
         else if (argv[i][1] == 'a' && argv[i][2] == '=')
         {
             pszArchString = argv[i] + 3;
@@ -1010,7 +1220,7 @@ int main(int argc, char *argv[])
 
         OutputHeader_asmstub(file, pszDllName);
         result = ParseFile(pszSource, file, OutputLine_asmstub);
-        fprintf(file, "\nEND\n");
+        fprintf(file, "\n    END\n");
         fclose(file);
     }