mirror of
https://github.com/reactos/reactos.git
synced 2024-08-17 08:58:30 +00:00
1630 lines
46 KiB
C
1630 lines
46 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdarg.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;
|
|
unsigned nStartVersion;
|
|
unsigned nEndVersion;
|
|
int bVersionIncluded;
|
|
} EXPORT;
|
|
|
|
#if 0 // Debug helper function
|
|
void
|
|
PrintExport(EXPORT *pexp)
|
|
{
|
|
fprintf(stderr, "strName='%.*s'\n", pexp->strName.len, pexp->strName.buf);
|
|
fprintf(stderr, "strName='%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
|
|
fprintf(stderr, "nCallingConvention=%u\n", pexp->nCallingConvention);
|
|
fprintf(stderr, "nOrdinal=%u\n", pexp->nOrdinal);
|
|
fprintf(stderr, "nStackBytes=%u\n", pexp->nStackBytes);
|
|
fprintf(stderr, "nArgCount=%u\n", pexp->nArgCount);
|
|
fprintf(stderr, "uFlags=0x%x\n", pexp->uFlags);
|
|
fprintf(stderr, "nNumber=%u\n", pexp->nNumber);
|
|
fprintf(stderr, "nStartVersion=%u\n", pexp->nStartVersion);
|
|
fprintf(stderr, "nEndVersion=%u\n", pexp->nEndVersion);
|
|
fprintf(stderr, "bVersionIncluded=%u\n", pexp->bVersionIncluded);
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
const char *
|
|
NextLine(const 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(const char *pc)
|
|
{
|
|
int length = 0;
|
|
|
|
while (!IsSeparator(*pc++)) length++;
|
|
|
|
return length;
|
|
}
|
|
|
|
const char *
|
|
NextToken(const 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");
|
|
}
|
|
|
|
/* __int128 is not supported on x86, so use a custom type */
|
|
fprintf(file, "\n"
|
|
"typedef struct {\n"
|
|
" __int64 lower;\n"
|
|
" __int64 upper;\n"
|
|
"} MyInt128;\n");
|
|
|
|
fprintf(file, "\n");
|
|
}
|
|
|
|
int
|
|
OutputLine_stub(FILE *file, EXPORT *pexp)
|
|
{
|
|
int i;
|
|
int bRelay = 0;
|
|
int bInPrototype = 0;
|
|
|
|
/* Workaround for forwarded externs. See here for an explanation:
|
|
* https://stackoverflow.com/questions/4060143/forwarding-data-in-a-dll */
|
|
if (gbMSComp &&
|
|
(pexp->nCallingConvention == CC_EXTERN) &&
|
|
(pexp->strTarget.buf != NULL) &&
|
|
(!!ScanToken(pexp->strTarget.buf, '.')))
|
|
{
|
|
fprintf(file, "#pragma comment(linker,\"/export:%s%.*s=%.*s,DATA\")\n\n",
|
|
gpszUnderscore, pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
|
|
return 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, so use a custom type */
|
|
case ARG_INT128: fprintf(file, "MyInt128"); 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, "0x%%\"PRIx64\""); break;
|
|
case ARG_INT128: fprintf(file, "0x%%\"PRIx64\"-0x%%\"PRIx64\""); 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: case ARG_PTR: case ARG_STR:
|
|
case ARG_WSTR: case ARG_DBL: case ARG_INT64:
|
|
fprintf(file, "a%d", i); break;
|
|
case ARG_INT128:
|
|
fprintf(file, "a%d.lower, a%d.upper", i, i); break;
|
|
case ARG_FLOAT:
|
|
fprintf(file, "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 = 0x%%\"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, "%s_stub_ordinal%d",
|
|
gpszUnderscore, pexp->nOrdinal);
|
|
}
|
|
else if (giArch != ARCH_X86)
|
|
{
|
|
/* Does the string already have stdcall decoration? */
|
|
const char *pcAt = ScanToken(pexp->strName.buf, '@');
|
|
if (pcAt && (pcAt < (pexp->strName.buf + pexp->strName.len)) &&
|
|
(pexp->strName.buf[0] == '_'))
|
|
{
|
|
/* Skip leading underscore and remove trailing decoration */
|
|
sprintf(szNameBuffer, "_stub_%.*s",
|
|
(int)(pcAt - pexp->strName.buf - 1),
|
|
pexp->strName.buf + 1);
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
char namebuffer[19];
|
|
|
|
if ((nNameLength == 1) && (pcName[0] == '@'))
|
|
{
|
|
sprintf(namebuffer, "ordinal%d", pexp->nOrdinal);
|
|
pcName = namebuffer;
|
|
nNameLength = strlen(namebuffer);
|
|
}
|
|
|
|
/* 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_");
|
|
PrintName(fileDest, pexp, &pexp->strName, 0);
|
|
}
|
|
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 &&
|
|
(pexp->strName.len > 1) &&
|
|
(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)
|
|
{
|
|
/* Don't add private exports to the import lib */
|
|
if (gbImportLib && (pexp->uFlags & FL_PRIVATE))
|
|
{
|
|
DbgPrint("OutputLine_def: skipping private export '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
|
|
return 1;
|
|
}
|
|
|
|
/* For MS linker, forwarded externs are managed via #pragma comment(linker,"/export:_data=org.data,DATA") */
|
|
if (gbMSComp && !gbImportLib && (pexp->nCallingConvention == CC_EXTERN) &&
|
|
(pexp->strTarget.buf != NULL) && !!ScanToken(pexp->strTarget.buf, '.'))
|
|
{
|
|
DbgPrint("OutputLine_def: skipping forwarded extern export '%.*s' ->'%.*s'...\n",
|
|
pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
|
|
return 1;
|
|
}
|
|
|
|
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);
|
|
|
|
/* On GCC builds we force ordinals */
|
|
if ((pexp->uFlags & FL_ORDINAL) || (!gbMSComp && !gbImportLib))
|
|
{
|
|
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;
|
|
}
|
|
|
|
void
|
|
Fatalv(
|
|
const char* filename,
|
|
unsigned nLine,
|
|
const char *pcLine,
|
|
const char *pc,
|
|
size_t errorlen,
|
|
const char *format,
|
|
va_list argptr)
|
|
{
|
|
unsigned i, errorpos, len;
|
|
const char* pcLineEnd;
|
|
|
|
/* Get the length of the line */
|
|
pcLineEnd = strpbrk(pcLine, "\r\n");
|
|
len = (unsigned)(pcLineEnd - pcLine);
|
|
|
|
if (pc == NULL)
|
|
{
|
|
pc = pcLine + len - 1;
|
|
errorlen = 1;
|
|
}
|
|
|
|
errorpos = (unsigned)(pc - pcLine);
|
|
|
|
/* Output the error message */
|
|
fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos);
|
|
vfprintf(stderr, format, argptr);
|
|
fprintf(stderr, "\n");
|
|
|
|
/* Output the line with the error */
|
|
fprintf(stderr, "> %.*s\n", len, pcLine);
|
|
|
|
if (errorlen == 0)
|
|
{
|
|
errorlen = TokenLength(pc);
|
|
}
|
|
|
|
for (i = 0; i < errorpos + 2; i++)
|
|
{
|
|
fprintf(stderr, " ");
|
|
}
|
|
for (i = 0; i < errorlen; i++)
|
|
{
|
|
fprintf(stderr, "~");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
exit(-1);
|
|
}
|
|
|
|
void
|
|
Fatal(
|
|
const char* filename,
|
|
unsigned nLine,
|
|
const char *pcLine,
|
|
const char *pc,
|
|
size_t errorlen,
|
|
const char *format,
|
|
...)
|
|
{
|
|
va_list argptr;
|
|
|
|
va_start(argptr, format);
|
|
Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr);
|
|
va_end(argptr);
|
|
}
|
|
|
|
EXPORT *
|
|
ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports)
|
|
{
|
|
EXPORT *pexports;
|
|
const char *pc, *pcLine;
|
|
int cLines, nLine;
|
|
EXPORT exp;
|
|
int included;
|
|
unsigned int i;
|
|
|
|
*cExports = 0;
|
|
|
|
//fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
|
|
|
|
/* Count the lines */
|
|
for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++)
|
|
{
|
|
/* Nothing */
|
|
}
|
|
|
|
/* Allocate an array of EXPORT structures */
|
|
pexports = malloc(cLines * sizeof(EXPORT));
|
|
if (pexports == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines);
|
|
return NULL;
|
|
}
|
|
|
|
/* Loop all lines */
|
|
nLine = 1;
|
|
exp.nNumber = 0;
|
|
for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
|
|
{
|
|
pc = pcLine;
|
|
|
|
exp.strName.buf = NULL;
|
|
exp.strName.len = 0;
|
|
exp.strTarget.buf = NULL;
|
|
exp.strTarget.len = 0;
|
|
exp.nArgCount = 0;
|
|
exp.uFlags = 0;
|
|
exp.nNumber++;
|
|
exp.nStartVersion = 0;
|
|
exp.nEndVersion = 0xFFFFFFFF;
|
|
exp.bVersionIncluded = 1;
|
|
|
|
/* Skip white spaces */
|
|
while (*pc == ' ' || *pc == '\t') pc++;
|
|
|
|
/* Check for line break or comment */
|
|
if ((*pc == '\r') || (*pc == '\n') ||
|
|
(*pc == ';') || (*pc == '#'))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* On EOF we are done */
|
|
if (*pc == 0)
|
|
{
|
|
return pexports;
|
|
}
|
|
|
|
/* Now we should get either an ordinal or @ */
|
|
if (*pc == '@')
|
|
{
|
|
exp.nOrdinal = -1;
|
|
}
|
|
else if ((*pc >= '0') && (*pc <= '9'))
|
|
{
|
|
char* end;
|
|
long int number = strtol(pc, &end, 10);
|
|
if ((*end != ' ') && (*end != '\t'))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal");
|
|
}
|
|
|
|
if ((number < 0) || (number > 0xFFFE))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal");
|
|
}
|
|
|
|
exp.nOrdinal = number;
|
|
|
|
/* The import lib should contain the ordinal only if -ordinal was specified */
|
|
if (!gbImportLib)
|
|
exp.uFlags |= FL_ORDINAL;
|
|
}
|
|
else
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal");
|
|
}
|
|
|
|
/* Go to next token (type) */
|
|
if (!(pc = NextToken(pc)))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
|
|
}
|
|
|
|
//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
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention");
|
|
}
|
|
|
|
/* Go to next token (options or name) */
|
|
if (!(pc = NextToken(pc)))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
|
|
}
|
|
|
|
/* Handle options */
|
|
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="))
|
|
{
|
|
const char *pcVersionStart = pc + 9;
|
|
|
|
/* Default to not included */
|
|
exp.bVersionIncluded = 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, (char**)&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, (char**)&pc, 16);
|
|
}
|
|
|
|
/* Check for degenerate range */
|
|
if (version > endversion)
|
|
{
|
|
Fatal(pszSourceFileName,
|
|
nLine,
|
|
pcLine,
|
|
pcVersionStart,
|
|
pc - pcVersionStart,
|
|
"Invalid version range");
|
|
}
|
|
|
|
exp.nStartVersion = version;
|
|
exp.nEndVersion = endversion;
|
|
|
|
/* Now compare the range with our version */
|
|
if ((guOsVersion >= version) &&
|
|
(guOsVersion <= endversion))
|
|
{
|
|
exp.bVersionIncluded = 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(stdout,
|
|
"INFO: %s line %d: Ignored option: '%.*s'\n",
|
|
pszSourceFileName,
|
|
nLine,
|
|
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) 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] == '@'))
|
|
{
|
|
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)))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
|
|
}
|
|
|
|
/* Verify syntax */
|
|
if (*pc++ != '(')
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('");
|
|
}
|
|
|
|
/* 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
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type");
|
|
}
|
|
|
|
exp.nArgCount++;
|
|
|
|
/* Go to next parameter */
|
|
if (!(pc = NextToken(pc)))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
|
|
}
|
|
}
|
|
|
|
/* Check syntax */
|
|
if (*pc++ != ')')
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'");
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @");
|
|
}
|
|
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))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition");
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal");
|
|
}
|
|
|
|
/*
|
|
* 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, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n",
|
|
pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
|
|
}
|
|
if (bHasOrdinal)
|
|
{
|
|
fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n",
|
|
pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pexports[*cExports] = exp;
|
|
(*cExports)++;
|
|
gbDebug = 0;
|
|
}
|
|
|
|
return pexports;
|
|
}
|
|
|
|
int
|
|
ApplyOrdinals(EXPORT* pexports, unsigned cExports)
|
|
{
|
|
unsigned short i, j;
|
|
char* used;
|
|
|
|
/* Allocate a table to mark used ordinals */
|
|
used = malloc(65536);
|
|
if (used == NULL)
|
|
{
|
|
fprintf(stderr, "Failed to allocate memory for ordinal use table\n");
|
|
return -1;
|
|
}
|
|
memset(used, 0, 65536);
|
|
|
|
/* Pass 1: mark the ordinals that are already used */
|
|
for (i = 0; i < cExports; i++)
|
|
{
|
|
if (pexports[i].uFlags & FL_ORDINAL)
|
|
{
|
|
if (used[pexports[i].nOrdinal] != 0)
|
|
{
|
|
fprintf(stderr, "Found duplicate ordinal: %u\n", pexports[i].nOrdinal);
|
|
return -1;
|
|
}
|
|
used[pexports[i].nOrdinal] = 1;
|
|
}
|
|
}
|
|
|
|
/* Pass 2: apply available ordinals */
|
|
for (i = 0, j = 1; i < cExports; i++)
|
|
{
|
|
if ((pexports[i].uFlags & FL_ORDINAL) == 0 && pexports[i].bVersionIncluded)
|
|
{
|
|
while (used[j] != 0)
|
|
j++;
|
|
|
|
pexports[i].nOrdinal = j;
|
|
used[j] = 1;
|
|
}
|
|
}
|
|
|
|
free(used);
|
|
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;
|
|
unsigned cExports = 0, i;
|
|
EXPORT *pexports;
|
|
|
|
if (argc < 2)
|
|
{
|
|
usage();
|
|
return -1;
|
|
}
|
|
|
|
/* Read options */
|
|
for (i = 1; i < (unsigned)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';
|
|
|
|
pexports = ParseFile(pszSource, file, &cExports);
|
|
if (pexports == NULL)
|
|
{
|
|
fprintf(stderr, "error: could not parse file!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!gbMSComp)
|
|
{
|
|
if (ApplyOrdinals(pexports, cExports) < 0)
|
|
{
|
|
fprintf(stderr, "error: could not apply ordinals!\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
for (i = 0; i < cExports; i++)
|
|
{
|
|
if (pexports[i].bVersionIncluded)
|
|
OutputLine_def(file, &pexports[i]);
|
|
}
|
|
|
|
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);
|
|
|
|
for (i = 0; i < cExports; i++)
|
|
{
|
|
if (pexports[i].bVersionIncluded)
|
|
OutputLine_stub(file, &pexports[i]);
|
|
}
|
|
|
|
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);
|
|
|
|
for (i = 0; i < cExports; i++)
|
|
{
|
|
if (pexports[i].bVersionIncluded)
|
|
OutputLine_asmstub(file, &pexports[i]);
|
|
}
|
|
|
|
fprintf(file, "\n END\n");
|
|
fclose(file);
|
|
}
|
|
|
|
free(pexports);
|
|
|
|
return 0;
|
|
}
|