reactos/sdk/tools/spec2def/spec2def.c
Hermès Bélusca-Maïto 2becb000c8
[SPEC2DEF] Check whether OLE-specific exports do not have assigned ordinals, as they should be.
The check is done at the same time as when we check also that these
exports are marked PRIVATE or not.

This permits us to check for the same conditions as MS' LINK.EXE, but
here on GCC builds as well.

See the following pages for more details:
https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-warning-lnk4104
https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-warning-lnk4222

In this last page (LNK4222), the specified list of exports is incomplete.
Manual tests showed that the very same list as for LNK4104 is checked.
2019-07-28 15:50:30 +02:00

1347 lines
38 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#ifdef _MSC_VER
#define strcasecmp(_String1, _String2) _stricmp(_String1, _String2)
#define strncasecmp(_String1, _String2, _MaxCount) _strnicmp(_String1, _String2, _MaxCount)
#endif
#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
typedef struct _STRING
{
const char *buf;
int len;
} STRING, *PSTRING;
typedef struct
{
STRING strName;
STRING strTarget;
int nCallingConvention;
int nOrdinal;
int nStackBytes;
int nArgCount;
int anArgs[30];
unsigned int uFlags;
int nNumber;
} EXPORT;
enum _ARCH
{
ARCH_X86,
ARCH_AMD64,
ARCH_IA64,
ARCH_ARM,
ARCH_PPC
};
typedef int (*PFNOUTLINE)(FILE *, EXPORT *);
int gbMSComp = 0;
int gbImportLib = 0;
int gbNotPrivateNoWarn = 0;
int gbTracing = 0;
int giArch = ARCH_X86;
char *pszArchString = "i386";
char *pszArchString2;
char *pszSourceFileName = NULL;
char *pszDllName = NULL;
char *gpszUnderscore = "";
int gbDebug;
unsigned guOsVersion = 0x502;
#define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__))
enum
{
FL_PRIVATE = 1,
FL_STUB = 2,
FL_NONAME = 4,
FL_ORDINAL = 8,
FL_NORELAY = 16,
FL_RET64 = 32,
FL_REGISTER = 64,
};
enum
{
CC_STDCALL,
CC_CDECL,
CC_FASTCALL,
CC_THISCALL,
CC_EXTERN,
CC_STUB,
};
enum
{
ARG_LONG,
ARG_PTR,
ARG_STR,
ARG_WSTR,
ARG_DBL,
ARG_INT64,
ARG_INT128,
ARG_FLOAT
};
const char* astrCallingConventions[] =
{
"STDCALL",
"CDECL",
"FASTCALL",
"THISCALL",
"EXTERN"
};
/*
* List of OLE exports that should be PRIVATE and not be assigned an ordinal.
* In case these conditions are not met when linking with MS LINK.EXE, warnings
* LNK4104 and LNK4222 respectively are emitted.
*/
static const char* astrOlePrivateExports[] =
{
"DllCanUnloadNow",
"DllGetClassObject",
"DllGetClassFactoryFromClassString",
"DllGetDocumentation",
"DllInitialize",
"DllInstall",
"DllRegisterServer",
"DllRegisterServerEx",
"DllRegisterServerExW",
"DllUnload",
"DllUnregisterServer",
"RasCustomDeleteEntryNotify",
"RasCustomDial",
"RasCustomDialDlg",
"RasCustomEntryDlg",
};
static
int
IsSeparator(char chr)
{
return ((chr <= ',' && chr != '$' && chr != '#') ||
(chr >= ':' && chr < '?') );
}
int
CompareToken(const char *token, const char *comparand)
{
while (*comparand)
{
if (*token != *comparand) return 0;
token++;
comparand++;
}
if (IsSeparator(comparand[-1])) return 1;
if (!IsSeparator(*token)) return 0;
return 1;
}
const char *
ScanToken(const char *token, char chr)
{
while (!IsSeparator(*token))
{
if (*token == chr) return token;
token++;
}
return 0;
}
char *
NextLine(char *pc)
{
while (*pc != 0)
{
if (pc[0] == '\n' && pc[1] == '\r') return pc + 2;
else if (pc[0] == '\n') return pc + 1;
pc++;
}
return pc;
}
int
TokenLength(char *pc)
{
int length = 0;
while (!IsSeparator(*pc++)) length++;
return length;
}
char *
NextToken(char *pc)
{
/* Skip token */
while (!IsSeparator(*pc)) pc++;
/* Skip white spaces */
while (*pc == ' ' || *pc == '\t') pc++;
/* Check for end of line */
if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0;
/* Check for comment */
if (*pc == '#' || *pc == ';') return 0;
return pc;
}
void
OutputHeader_stub(FILE *file)
{
fprintf(file, "/* This file is autogenerated, do not edit. */\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)
{
/* Only relay trace stdcall C functions */
if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL)
|| (pexp->uFlags & FL_NORELAY)
|| (pexp->strName.buf[0] == '?'))
{
return 0;
}
bRelay = 1;
}
/* Declare the "real" function */
if (bRelay)
{
fprintf(file, "extern ");
bInPrototype = 1;
}
do
{
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 ");
}
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;
/* __int128 is not supported on x86, and int128 in spec files most often represents a GUID */
case ARG_INT128 : fprintf(file, "GUID"); break;
case ARG_FLOAT: fprintf(file, "float"); break;
}
fprintf(file, " a%d", i);
}
if (bInPrototype)
{
fprintf(file, ");\n\n");
}
} while (bInPrototype--);
if (!bRelay)
{
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)
{
fprintf(file, "\t__int64 retval;\n");
}
else
{
fprintf(file, "\tint retval;\n");
}
fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(",
pszDllName, 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, "0x%%lx"); break;
case ARG_PTR: fprintf(file, "0x%%p"); break;
case ARG_STR: fprintf(file, "'%%s'"); break;
case ARG_WSTR: fprintf(file, "'%%ws'"); break;
case ARG_DBL: fprintf(file, "%%f"); break;
case ARG_INT64: fprintf(file, "%%\"PRIx64\""); break;
case ARG_INT128: fprintf(file, "'%%s'"); break;
case ARG_FLOAT: fprintf(file, "%%f"); break;
}
}
fprintf(file, ")\\n\"");
for (i = 0; i < pexp->nArgCount; i++)
{
fprintf(file, ", ");
switch (pexp->anArgs[i])
{
case ARG_LONG: fprintf(file, "(long)a%d", i); break;
case ARG_PTR: fprintf(file, "(void*)a%d", i); break;
case ARG_STR: fprintf(file, "(char*)a%d", i); break;
case ARG_WSTR: fprintf(file, "(wchar_t*)a%d", i); break;
case ARG_DBL: fprintf(file, "(double)a%d", i); break;
case ARG_INT64: fprintf(file, "(__int64)a%d", i); break;
case ARG_INT128: fprintf(file, "wine_dbgstr_guid(&a%d)", i); break;
case ARG_FLOAT: fprintf(file, "(float)a%d", i); break;
}
}
fprintf(file, ");\n");
if (pexp->nCallingConvention == CC_STUB)
{
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);
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;
}
void
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.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");
}
}
void
Output_stublabel(FILE *fileDest, char* pszSymbolName)
{
if (giArch == ARCH_ARM)
{
fprintf(fileDest,
"\tEXPORT |%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] == '@')
{
sprintf(szNameBuffer, "%sordinal%d\n%sordinal%d: nop\n",
gpszUnderscore, pexp->nOrdinal, gpszUnderscore, pexp->nOrdinal);
}
else if (giArch != ARCH_X86)
{
sprintf(szNameBuffer, "_stub_%.*s",
pexp->strName.len, pexp->strName.buf);
}
else if (pexp->nCallingConvention == CC_STDCALL)
{
sprintf(szNameBuffer, "__stub_%.*s@%d",
pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
}
else if (pexp->nCallingConvention == CC_FASTCALL)
{
sprintf(szNameBuffer, "@_stub_%.*s@%d",
pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
}
else if ((pexp->nCallingConvention == CC_CDECL) ||
(pexp->nCallingConvention == CC_THISCALL) ||
(pexp->nCallingConvention == CC_EXTERN) ||
(pexp->nCallingConvention == CC_STUB))
{
sprintf(szNameBuffer, "__stub_%.*s",
pexp->strName.len, pexp->strName.buf);
}
else
{
fprintf(stderr, "Invalid calling convention");
return 0;
}
Output_stublabel(fileDest, szNameBuffer);
return 1;
}
void
OutputHeader_def(FILE *file, char *libname)
{
fprintf(file,
"; File generated automatically, do not edit!\n\n"
"NAME %s\n\n"
"EXPORTS\n",
libname);
}
void
PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
{
const char *pcName = pstr->buf;
int nNameLength = pstr->len;
const char* pcDot, *pcAt;
/* 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 = (int)(pcAt - pcName);
}
/* Print the undecorated function name */
fprintf(fileDest, "%.*s", nNameLength, pcName);
}
else if (fDeco &&
((pexp->nCallingConvention == CC_STDCALL) ||
(pexp->nCallingConvention == CC_FASTCALL)))
{
/* Scan for a dll forwarding dot */
pcDot = ScanToken(pcName, '.');
if (pcDot)
{
/* First print the dll name, followed by a dot */
nNameLength = (int)(pcDot - pcName);
fprintf(fileDest, "%.*s.", nNameLength, pcName);
/* Now the actual function name */
pcName = pcDot + 1;
nNameLength = pexp->strTarget.len - nNameLength - 1;
}
/* Does the string already have decoration? */
pcAt = ScanToken(pcName, '@');
if (pcAt && (pcAt < (pcName + nNameLength)))
{
/* On GCC, we need to remove the leading stdcall underscore */
if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL))
{
pcName++;
nNameLength--;
}
/* Print the already decorated function name */
fprintf(fileDest, "%.*s", nNameLength, pcName);
}
else
{
/* Print the prefix, but skip it for (GCC && stdcall) */
if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL))
{
fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_');
}
/* Print the name with trailing decoration */
fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes);
}
}
else
{
/* Print the undecorated function name */
fprintf(fileDest, "%.*s", nNameLength, pcName);
}
}
void
OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
{
PrintName(fileDest, pexp, &pexp->strName, 0);
if (gbImportLib)
{
/* Redirect to a stub function, to get the right decoration in the lib */
fprintf(fileDest, "=_stub_%.*s", pexp->strName.len, pexp->strName.buf);
}
else if (pexp->strTarget.buf)
{
if (pexp->strName.buf[0] == '?')
{
//fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n",
// pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
}
else
{
fprintf(fileDest, "=");
/* If the original name was decorated, use decoration in the forwarder as well */
if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') &&
!ScanToken(pexp->strTarget.buf, '@') &&
((pexp->nCallingConvention == CC_STDCALL) ||
(pexp->nCallingConvention == CC_FASTCALL)) )
{
PrintName(fileDest, pexp, &pexp->strTarget, 1);
}
else
{
/* Write the undecorated redirection name */
fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf);
}
}
}
else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
(pexp->strName.buf[0] == '?'))
{
/* 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);
/* Check if this is a forwarded export */
if (pexp->strTarget.buf)
{
int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.');
DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
/* print the target name, don't decorate if it is external */
fprintf(fileDest, "=");
PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal);
}
else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
(pexp->strName.buf[0] == '?'))
{
/* 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) &&
((pexp->nCallingConvention == CC_STDCALL) ||
(pexp->nCallingConvention == CC_FASTCALL)))
{
/* Is this the import lib? */
if (gbImportLib)
{
/* Is the name in the spec file decorated? */
const char* pcDeco = ScanToken(pexp->strName.buf, '@');
if (pcDeco && (pcDeco < pexp->strName.buf + pexp->strName.len))
{
/* Write the name including the leading @ */
fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
}
}
else if ((!pexp->strTarget.buf) && !(bTracing))
{
/* Write a forwarder to the actual decorated symbol */
fprintf(fileDest, "=");
PrintName(fileDest, pexp, &pexp->strName, 1);
}
}
}
int
OutputLine_def(FILE *fileDest, EXPORT *pexp)
{
DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
fprintf(fileDest, " ");
if (gbMSComp)
OutputLine_def_MS(fileDest, pexp);
else
OutputLine_def_GCC(fileDest, pexp);
if (pexp->uFlags & FL_ORDINAL)
{
fprintf(fileDest, " @%d", pexp->nOrdinal);
}
if (pexp->uFlags & FL_NONAME)
{
fprintf(fileDest, " NONAME");
}
/* Either PRIVATE or DATA */
if (pexp->uFlags & FL_PRIVATE)
{
fprintf(fileDest, " PRIVATE");
}
else if (pexp->nCallingConvention == CC_EXTERN)
{
fprintf(fileDest, " DATA");
}
fprintf(fileDest, "\n");
return 1;
}
int
ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
{
char *pc, *pcLine;
int nLine;
EXPORT exp;
int included, version_included;
char namebuffer[16];
unsigned int i;
//fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
/* Loop all lines */
nLine = 1;
exp.nNumber = 0;
for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
{
pc = pcLine;
exp.nArgCount = 0;
exp.uFlags = 0;
exp.nNumber++;
/* Skip white spaces */
while (*pc == ' ' || *pc == '\t') pc++;
/* Skip empty lines, stop at EOF */
if (*pc == ';' || *pc <= '#') continue;
if (*pc == 0) return 0;
/* Now we should get either an ordinal or @ */
if (*pc == '@')
exp.nOrdinal = -1;
else
{
exp.nOrdinal = atol(pc);
/* The import lib should contain the ordinal only if -ordinal was specified */
if (!gbImportLib)
exp.uFlags |= FL_ORDINAL;
}
/* Go to next token (type) */
if (!(pc = NextToken(pc)))
{
fprintf(stderr, "%s line %d: error: unexpected end of line\n", pszSourceFileName, nLine);
return -10;
}
//fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
/* Now we should get the type */
if (CompareToken(pc, "stdcall"))
{
exp.nCallingConvention = CC_STDCALL;
}
else if (CompareToken(pc, "cdecl") ||
CompareToken(pc, "varargs"))
{
exp.nCallingConvention = CC_CDECL;
}
else if (CompareToken(pc, "fastcall"))
{
exp.nCallingConvention = CC_FASTCALL;
}
else if (CompareToken(pc, "thiscall"))
{
exp.nCallingConvention = CC_THISCALL;
}
else if (CompareToken(pc, "extern"))
{
exp.nCallingConvention = CC_EXTERN;
}
else if (CompareToken(pc, "stub"))
{
exp.nCallingConvention = CC_STUB;
}
else
{
fprintf(stderr, "%s line %d: error: expected callconv, got '%.*s' %d\n",
pszSourceFileName, nLine, TokenLength(pc), pc, *pc);
return -11;
}
/* Go to next token (options or name) */
if (!(pc = NextToken(pc)))
{
fprintf(stderr, "fail2\n");
return -12;
}
/* Handle options */
included = 1;
version_included = 1;
while (*pc == '-')
{
if (CompareToken(pc, "-arch="))
{
/* Default to not included */
included = 0;
pc += 5;
/* Look if we are included */
do
{
pc++;
if (CompareToken(pc, pszArchString) ||
CompareToken(pc, pszArchString2))
{
included = 1;
}
/* Skip to next arch or end */
while (*pc > ',') pc++;
} while (*pc == ',');
}
else if (CompareToken(pc, "-i386"))
{
if (giArch != ARCH_X86) included = 0;
}
else if (CompareToken(pc, "-version="))
{
/* Default to not included */
version_included = 0;
pc += 8;
/* Look if we are included */
do
{
unsigned version, endversion;
/* Optionally skip leading '0x' */
pc++;
if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
/* Now get the version number */
endversion = version = strtoul(pc, &pc, 16);
/* Check if it's a range */
if (pc[0] == '+')
{
endversion = 0xFFF;
pc++;
}
else if (pc[0] == '-')
{
/* Optionally skip leading '0x' */
pc++;
if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
endversion = strtoul(pc, &pc, 16);
}
/* Check for degenerate range */
if (version > endversion)
{
fprintf(stderr, "%s line %d: error: invalid version rangen\n", pszSourceFileName, nLine);
return -1;
}
/* Now compare the range with our version */
if ((guOsVersion >= version) &&
(guOsVersion <= endversion))
{
version_included = 1;
}
/* Skip to next arch or end */
while (*pc > ',') pc++;
} while (*pc == ',');
}
else if (CompareToken(pc, "-private"))
{
exp.uFlags |= FL_PRIVATE;
}
else if (CompareToken(pc, "-noname"))
{
exp.uFlags |= FL_ORDINAL | FL_NONAME;
}
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"))
{
exp.uFlags |= FL_NORELAY;
}
else if (CompareToken(pc, "-ret64"))
{
exp.uFlags |= FL_RET64;
}
else if (CompareToken(pc, "-register"))
{
exp.uFlags |= FL_REGISTER;
}
else
{
fprintf(stderr, "info: ignored option: '%.*s'\n",
TokenLength(pc), pc);
}
/* Go to next token */
pc = NextToken(pc);
}
//fprintf(stderr, "info: Name:'%.10s'\n", pc);
/* If arch didn't match ours, skip this entry */
if (!included || !version_included) continue;
/* 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] == '@'))
{
sprintf(namebuffer, "ordinal%d", exp.nOrdinal);
exp.strName.len = strlen(namebuffer);
exp.strName.buf = namebuffer;
exp.uFlags |= FL_ORDINAL | FL_NONAME;
}
/* Handle parameters */
exp.nStackBytes = 0;
if (exp.nCallingConvention != CC_EXTERN &&
exp.nCallingConvention != CC_STUB)
{
/* Go to next token */
if (!(pc = NextToken(pc)))
{
fprintf(stderr, "%s line %d: error: expected token\n", pszSourceFileName, nLine);
return -13;
}
/* Verify syntax */
if (*pc++ != '(')
{
fprintf(stderr, "%s line %d: error: expected '('\n", pszSourceFileName, nLine);
return -14;
}
/* Skip whitespaces */
while (*pc == ' ' || *pc == '\t') pc++;
exp.nStackBytes = 0;
while (*pc >= '0')
{
if (CompareToken(pc, "long"))
{
exp.nStackBytes += 4;
exp.anArgs[exp.nArgCount] = ARG_LONG;
}
else if (CompareToken(pc, "double"))
{
exp.nStackBytes += 8;
exp.anArgs[exp.nArgCount] = ARG_DBL;
}
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_WSTR;
}
else if (CompareToken(pc, "int64"))
{
exp.nStackBytes += 8;
exp.anArgs[exp.nArgCount] = ARG_INT64;
}
else if (CompareToken(pc, "int128"))
{
exp.nStackBytes += 16;
exp.anArgs[exp.nArgCount] = ARG_INT128;
}
else if (CompareToken(pc, "float"))
{
exp.nStackBytes += 4;
exp.anArgs[exp.nArgCount] = ARG_FLOAT;
}
else
fprintf(stderr, "%s line %d: error: expected type, got: %.10s\n", pszSourceFileName, nLine, pc);
exp.nArgCount++;
/* Go to next parameter */
if (!(pc = NextToken(pc)))
{
fprintf(stderr, "fail5\n");
return -15;
}
}
/* Check syntax */
if (*pc++ != ')')
{
fprintf(stderr, "%s line %d: error: expected ')'\n", pszSourceFileName, nLine);
return -16;
}
}
/* Handle special stub cases */
if (exp.nCallingConvention == CC_STUB)
{
/* Check for c++ mangled name */
if (pc[0] == '?')
{
//printf("Found c++ mangled name...\n");
//
}
else
{
/* Check for stdcall name */
const char *p = ScanToken(pc, '@');
if (p && (p - pc < exp.strName.len))
{
int i;
/* Truncate the name to before the @ */
exp.strName.len = (int)(p - pc);
if (exp.strName.len < 1)
{
fprintf(stderr, "%s line %d: error: unexpected @ found\n", pszSourceFileName, nLine);
return -1;
}
exp.nStackBytes = atoi(p + 1);
exp.nArgCount = exp.nStackBytes / 4;
exp.nCallingConvention = CC_STDCALL;
exp.uFlags |= FL_STUB;
for (i = 0; i < exp.nArgCount; i++)
exp.anArgs[i] = ARG_LONG;
}
}
}
/* Get optional redirection */
pc = NextToken(pc);
if (pc)
{
exp.strTarget.buf = pc;
exp.strTarget.len = TokenLength(pc);
/* Check syntax (end of line) */
if (NextToken(pc))
{
fprintf(stderr, "%s line %d: error: additional tokens after ')'\n", pszSourceFileName, nLine);
return -17;
}
/* Don't relay-trace forwarded functions */
exp.uFlags |= FL_NORELAY;
}
else
{
exp.strTarget.buf = NULL;
exp.strTarget.len = 0;
}
/* Check for no-name without ordinal */
if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
{
fprintf(stderr, "%s line %d: error: ordinal export without ordinal!\n", pszSourceFileName, nLine);
return -1;
}
/*
* Check for special handling of OLE exports, only when MSVC
* is not used, since otherwise this is handled by MS LINK.EXE.
*/
if (!gbMSComp)
{
/* Check whether the current export is not PRIVATE, or has an ordinal */
int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE));
int bHasOrdinal = (exp.uFlags & FL_ORDINAL);
/* Check whether the current export is an OLE export, in case any of these tests pass */
if (bIsNotPrivate || bHasOrdinal)
{
for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i)
{
if (strlen(astrOlePrivateExports[i]) == exp.strName.len &&
strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0)
{
/* The current export is an OLE export: display the corresponding warning */
if (bIsNotPrivate)
{
fprintf(stderr, "%s line %d: warning: exported symbol '%.*s' should be PRIVATE\n",
pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
}
if (bHasOrdinal)
{
fprintf(stderr, "%s line %d: warning: exported symbol '%.*s' should not be assigned an ordinal\n",
pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
}
break;
}
}
}
}
OutputLine(fileDest, &exp);
gbDebug = 0;
}
return 0;
}
void usage(void)
{
printf("syntax: spec2def [<options> ...] <spec file>\n"
"Possible options:\n"
" -h --help print this help screen\n"
" -l=<file> generate an asm lib stub\n"
" -d=<file> generate a def file\n"
" -s=<file> generate 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"
" --no-private-warnings suppress warnings about symbols that should be -private\n"
" -a=<arch> set architecture to <arch> (i386, x86_64, arm)\n"
" --with-tracing generate wine-like \"+relay\" trace trampolines (needs -s)\n");
}
int main(int argc, char *argv[])
{
size_t nFileSize;
char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL;
const char* pszVersionOption = "--version=0x";
char achDllName[40];
FILE *file;
int result = 0, i;
if (argc < 2)
{
usage();
return -1;
}
/* Read options */
for (i = 1; i < argc && *argv[i] == '-'; i++)
{
if ((strcasecmp(argv[i], "--help") == 0) ||
(strcasecmp(argv[i], "-h") == 0))
{
usage();
return 0;
}
else if (argv[i][1] == 'd' && argv[i][2] == '=')
{
pszDefFileName = argv[i] + 3;
}
else if (argv[i][1] == 'l' && argv[i][2] == '=')
{
pszLibStubName = argv[i] + 3;
}
else if (argv[i][1] == 's' && argv[i][2] == '=')
{
pszStubFileName = argv[i] + 3;
}
else if (argv[i][1] == 'n' && argv[i][2] == '=')
{
pszDllName = argv[i] + 3;
}
else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0)
{
guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16);
}
else if (strcasecmp(argv[i], "--implib") == 0)
{
gbImportLib = 1;
}
else if (strcasecmp(argv[i], "--ms") == 0)
{
gbMSComp = 1;
}
else if (strcasecmp(argv[i], "--no-private-warnings") == 0)
{
gbNotPrivateNoWarn = 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;
}
else
{
fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
return -1;
}
}
if (strcasecmp(pszArchString, "i386") == 0)
{
giArch = ARCH_X86;
gpszUnderscore = "_";
}
else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
{
pszArchString2 = "win64";
}
else
pszArchString2 = "win32";
/* Set a default dll name */
if (!pszDllName)
{
char *p1, *p2;
size_t len;
p1 = strrchr(argv[i], '\\');
if (!p1) p1 = strrchr(argv[i], '/');
p2 = p1 = p1 ? p1 + 1 : argv[i];
/* walk up to '.' */
while (*p2 != '.' && *p2 != 0) p2++;
len = p2 - p1;
if (len >= sizeof(achDllName) - 5)
{
fprintf(stderr, "name too long: %s\n", p1);
return -2;
}
strncpy(achDllName, p1, len);
strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
pszDllName = achDllName;
}
/* Open input file */
pszSourceFileName = argv[i];
file = fopen(pszSourceFileName, "r");
if (!file)
{
fprintf(stderr, "error: could not open file %s\n", pszSourceFileName);
return -3;
}
/* Get file size */
fseek(file, 0, SEEK_END);
nFileSize = ftell(file);
rewind(file);
/* Allocate memory buffer */
pszSource = malloc(nFileSize + 1);
if (!pszSource)
{
fclose(file);
return -4;
}
/* Load input file into memory */
nFileSize = fread(pszSource, 1, nFileSize, file);
fclose(file);
/* Zero terminate the source */
pszSource[nFileSize] = '\0';
if (pszDefFileName)
{
/* Open output file */
file = fopen(pszDefFileName, "w");
if (!file)
{
fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
return -5;
}
OutputHeader_def(file, pszDllName);
result = ParseFile(pszSource, file, OutputLine_def);
fclose(file);
}
if (pszStubFileName)
{
/* Open output file */
file = fopen(pszStubFileName, "w");
if (!file)
{
fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
return -5;
}
OutputHeader_stub(file);
result = ParseFile(pszSource, file, OutputLine_stub);
fclose(file);
}
if (pszLibStubName)
{
/* Open output file */
file = fopen(pszLibStubName, "w");
if (!file)
{
fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
return -5;
}
OutputHeader_asmstub(file, pszDllName);
result = ParseFile(pszSource, file, OutputLine_asmstub);
fprintf(file, "\n END\n");
fclose(file);
}
return result;
}