VBoxDef2LazyLoad.cpp revision 3d4a63e5868109272b52596444fae45e240f1ad9
/* $Id$ */
/** @file
* VBoxDef2LazyLoad - Lazy Library Loader Generator.
*
* @note Only tested on win.amd64 & darwin.amd64.
*/
/*
* Copyright (C) 2013-2015 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct MYEXPORT
{
bool fNoName;
unsigned uOrdinal;
char szName[1];
} MYEXPORT;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** @name Options
* @{ */
static const char *g_pszOutput = NULL;
static const char *g_pszLibrary = NULL;
static unsigned g_cInputs = 0;
static bool g_fIgnoreData = true;
static bool g_fWithExplictLoadFunction = false;
/** @} */
/** Pointer to the export name list head. */
/** Pointer to the next pointer for insertion. */
{
psz++;
return psz;
}
{
psz++;
return psz;
}
static unsigned wordLength(const char *pszWord)
{
unsigned off = 0;
char ch;
&& ch != '='
&& ch != ','
&& ch != ':'
off++;
return off;
}
/**
* Parses the module definition file, collecting export information.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
* details has been displayed.
* @param pInput The input stream.
*/
{
/*
* Process the file line-by-line.
*/
bool fInExports = false;
unsigned iLine = 0;
char szLine[16384];
{
iLine++;
/*
* Strip leading and trailing spaces from the line as well as
* trailing comments.
*/
if (*psz == ';')
continue; /* comment line. */
if (pszComment)
*pszComment = '\0';
if (!cch)
continue;
/*
* Check for known directives.
*/
{
fInExports = true;
/* In case there is an export on the same line. (Really allowed?) */
if (!*psz)
continue;
}
/* Directives that we don't care about, but need to catch in order to
terminate the EXPORTS section in a timely manner. */
)
{
fInExports = false;
}
/*
* Process exports:
* entryname[=internalname] [@ordinal[ ][NONAME]] [DATA] [PRIVATE]
*/
if (fInExports)
{
if (*psz == '=')
{
}
bool fNoName = true;
unsigned uOrdinal = ~0U;
if (*psz == '@')
{
psz++;
{
return RTEXITCODE_FAILURE;
}
{
uOrdinal *= 10;
}
{
#if 0
fNoName = true;
#else
return RTEXITCODE_FAILURE;
#endif
}
}
while (*psz)
{
{
if (!g_fIgnoreData)
{
return RTEXITCODE_SUCCESS;
}
}
{
return RTEXITCODE_SUCCESS;
}
}
/*
* Add the export.
*/
if (!pExp)
{
return RTEXITCODE_SUCCESS;
}
*g_ppExpNext = pExp;
}
}
/*
* Why did we quit the loop, EOF or error?
*/
return RTEXITCODE_SUCCESS;
return RTEXITCODE_FAILURE;
}
/**
* Parses a_apszInputs, populating the list pointed to by g_pExpHead.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
* details has been displayed.
*/
static RTEXITCODE parseInputs(void)
{
for (unsigned i = 0; i < g_cInputs; i++)
{
if (pInput)
{
{
}
if (rcExit2 != RTEXITCODE_SUCCESS)
}
else
{
}
}
return rcExit;
}
/**
* Generates the assembly source code, writing it to @a pOutput.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
* details has been displayed.
* @param pOutput The output stream (caller checks it for errors
* when closing).
*/
{
for (unsigned i = 0; i < g_cInputs; i++)
";; DO NOT EDIT!\n"
";;\n"
"\n"
"\n"
"%%include \"iprt/asmdefs.mac\"\n"
"\n"
"\n");
/*
* Put the thunks first for alignment and other reasons. It's the hot part of the code.
*/
";\n"
"; Thunks.\n"
";\n"
"BEGINCODE\n");
"BEGINPROC %s\n"
" jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"
"ENDPROC %s\n",
"\n"
"\n");
/*
* Import pointers
*/
";\n"
"; Import pointers. Initialized to point a lazy loading stubs.\n"
";\n"
"BEGINDATA\n"
"g_apfnImports:\n");
"%%ifdef ASM_FORMAT_PE\n"
"global __imp_%s\n"
"__imp_%s:\n"
"%%endif\n"
"g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n",
"RTCCPTR_DEF 0 ; Terminator entry for traversal.\n"
"\n"
"\n");
/*
* Now for the less important stuff, starting with the names.
*
* We keep the names separate so we can traverse them in parallel to
* g_apfnImports in the load-everything routine further down.
*/
";\n"
"; Imported names.\n"
";\n"
"BEGINCODE\n"
"g_szLibrary db '%s',0\n"
"g_szzNames:\n",
"g_EndOfNames: db 0\n"
"\n"
"\n");
/*
* The per import lazy load code.
*/
";\n"
"; Lazy load+resolve stubs.\n"
";\n"
"BEGINCODE\n");
"___LazyLoad___%s:\n"
/* "int3\n" */
"%%ifdef RT_ARCH_AMD64\n"
" lea rax, [g_sz%s wrt rip]\n"
" lea r10, [g_pfn%s wrt rip]\n"
"%%elifdef RT_ARCH_X86\n"
" push g_sz%s\n"
" push g_pfn%s\n"
"%%else\n"
" %%error \"Unsupported architecture\"\n"
"%%endif\n"
" call LazyLoadResolver\n"
"%%ifdef RT_ARCH_X86\n"
" add esp, 8h\n"
"%%endif\n"
" jmp NAME(%s)\n"
"\n"
,
"\n"
"\n"
"\n");
/*
* The code that does the loading and resolving.
*/
";\n"
"; The module handle.\n"
";\n"
"BEGINDATA\n"
"g_hMod RTCCPTR_DEF 0\n"
"\n"
"\n"
"\n");
/*
* How we load the module needs to be selectable later on.
*
* saved all necessary registers.
*/
";\n"
";SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod,\n"
"; uint32_t fFlags, PRTERRINFO pErrInfo);\n"
";\n"
"EXTERN_IMP2 SUPR3HardenedLdrLoadAppPriv\n"
"BEGINCODE\n"
"\n"
"LazyLoading:\n"
" mov xCX, [g_hMod xWrtRIP]\n"
" or xCX, xCX\n"
" jnz .return\n"
"\n"
"%%ifdef ASM_CALL64_GCC\n"
" xor rcx, rcx ; pErrInfo\n"
" xor rdx, rdx ; fFlags (local load)\n"
" lea rsi, [g_hMod wrt rip] ; phLdrMod\n"
" lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"
" sub rsp, 08h\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
" add rsp, 08h\n"
"\n"
"%%elifdef ASM_CALL64_MSC\n"
" xor r9, r9 ; pErrInfo\n"
" xor r8, r8 ; fFlags (local load)\n"
" lea rdx, [g_hMod wrt rip] ; phLdrMod\n"
" lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"
" sub rsp, 28h\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
" add rsp, 28h\n"
"\n"
"%%elifdef RT_ARCH_X86\n"
" sub xSP, 0ch\n"
" push 0 ; pErrInfo\n"
" push 0 ; fFlags (local load)\n"
" push g_hMod ; phLdrMod\n"
" push g_szLibrary ; pszFilename\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
" add esp, 1ch\n"
"%%else\n"
" %%error \"Unsupported architecture\"\n"
"%%endif\n"
" or eax, eax\n"
" jz .loadok\n"
".badload:\n"
" int3\n"
" jmp .badload\n"
".loadok:\n"
" mov xCX, [g_hMod xWrtRIP]\n"
".return:\n"
" ret\n"
"LazyLoading_End:\n"
"\n"
"\n");
";\n"
";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n"
";\n"
"EXTERN_IMP2 RTLdrGetSymbol\n"
"BEGINCODE\n"
"LazyLoadResolver:\n"
"%%ifdef RT_ARCH_AMD64\n"
" push rbp\n"
" mov rbp, rsp\n"
" push r15\n"
" push r14\n"
" mov r15, rax ; name\n"
" mov r14, r10 ; ppfn\n"
" push r9\n"
" push r8\n"
" push rcx\n"
" push rdx\n"
" push r12\n"
" %%ifdef ASM_CALL64_GCC\n"
" push rsi\n"
" push rdi\n"
" mov r12, rsp\n"
" %%else\n"
" mov r12, rsp\n"
" sub rsp, 20h\n"
" %%endif\n"
" and rsp, 0fffffff0h ; Try make sure the stack is aligned\n"
"\n"
" call LazyLoading ; returns handle in rcx\n"
" %%ifdef ASM_CALL64_GCC\n"
" mov rdi, rcx ; hLdrMod\n"
" mov rsi, r15 ; pszSymbol\n"
" mov rdx, r14 ; ppvValue\n"
" %%else\n"
" mov rdx, r15 ; pszSymbol\n"
" mov r8, r14 ; ppvValue\n"
" %%endif\n"
" call IMP2(RTLdrGetSymbol)\n"
" or eax, eax\n"
" jz .symok\n"
".badsym:\n"
" int3\n"
" jmp .badsym\n"
".symok:\n"
"\n"
" mov rsp, r12\n"
" %%ifdef ASM_CALL64_GCC\n"
" pop rdi\n"
" pop rsi\n"
" %%endif\n"
" pop r12\n"
" pop rdx\n"
" pop rcx\n"
" pop r8\n"
" pop r9\n"
" pop r14\n"
" pop r15\n"
" leave\n"
"\n"
"%%elifdef RT_ARCH_X86\n"
" push ebp\n"
" mov ebp, esp\n"
" push eax\n"
" push ecx\n"
" push edx\n"
" and esp, 0fffffff0h\n"
"\n"
".loaded:\n"
" mov eax, [ebp + 4] ; value addr\n"
" push eax\n"
" mov edx, [ebp + 8] ; symbol name\n"
" push edx\n"
" call LazyLoading ; returns handle in ecx\n"
" mov ecx, [g_hMod]\n"
" call IMP2(RTLdrGetSymbol)\n"
" or eax, eax\n"
" jz .symok\n"
".badsym:\n"
" int3\n"
" jmp .badsym\n"
".symok:\n"
" lea esp, [ebp - 0ch]\n"
" pop edx\n"
" pop ecx\n"
" pop eax\n"
" leave\n"
"%%else\n"
" %%error \"Unsupported architecture\"\n"
"%%endif\n"
" ret\n"
"LazyLoadResolver_End:\n"
"\n"
"\n"
);
/*
* C callable method for explicitly loading the library and optionally
* resolving all the imports.
*/
{
int cchLibBaseName = (int)(strchr(g_pszLibrary, '.') ? strchr(g_pszLibrary, '.') - g_pszLibrary : strlen(g_pszLibrary));
";;\n"
"; ExplicitlyLoad%.*s(bool fResolveAllImports, pErrInfo);\n"
";\n"
"EXTERN_IMP2 RTErrInfoSet\n"
"BEGINCODE\n"
"BEGINPROC ExplicitlyLoad%.*s\n"
" push xBP\n"
" mov xBP, xSP\n"
" push xBX\n"
"%%ifdef ASM_CALL64_GCC\n"
" %%define pszCurStr r14\n"
" push r14\n"
"%%else\n"
" %%define pszCurStr xDI\n"
" push xDI\n"
"%%endif\n"
" sub xSP, 40h\n"
"\n"
" ;\n"
" ; Save parameters on stack (64-bit only).\n"
" ;\n"
"%%ifdef ASM_CALL64_GCC\n"
" mov [xBP - xCB * 3], rdi ; fResolveAllImports\n"
" mov [xBP - xCB * 4], rsi ; pErrInfo\n"
"%%elifdef ASM_CALL64_MSC\n"
" mov [xBP - xCB * 3], rcx ; fResolveAllImports\n"
" mov [xBP - xCB * 4], rdx ; pErrInfo\n"
"%%endif\n"
"\n"
" ;\n"
" ; Is the module already loaded?\n"
" ;\n"
" cmp RTCCPTR_PRE [g_hMod xWrtRIP], 0\n"
" jnz .loaded\n"
"\n"
" ;\n"
" ; Load the module.\n"
" ;\n"
"%%ifdef ASM_CALL64_GCC\n"
" mov rcx, [xBP - xCB * 4] ; pErrInfo\n"
" xor rdx, rdx ; fFlags (local load)\n"
" lea rsi, [g_hMod wrt rip] ; phLdrMod\n"
" lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
"\n"
"%%elifdef ASM_CALL64_MSC\n"
" mov r9, [xBP - xCB * 4] ; pErrInfo\n"
" xor r8, r8 ; fFlags (local load)\n"
" lea rdx, [g_hMod wrt rip] ; phLdrMod\n"
" lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
"\n"
"%%elifdef RT_ARCH_X86\n"
" sub xSP, 0ch\n"
" push dword [xBP + 12] ; pErrInfo\n"
" push 0 ; fFlags (local load)\n"
" push g_hMod ; phLdrMod\n"
" push g_szLibrary ; pszFilename\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
" add esp, 1ch\n"
"%%else\n"
" %%error \"Unsupported architecture\"\n"
"%%endif\n"
" or eax, eax\n"
" jnz .return\n"
"\n"
" ;\n"
" ; Resolve the imports too if requested to do so.\n"
" ;\n"
".loaded:\n"
"%%ifdef ASM_ARCH_X86\n"
" cmp byte [xBP + 8], 0\n"
"%%else\n"
" cmp byte [xBP - xCB * 3], 0\n"
"%%endif\n"
" je .return\n"
"\n"
" lea pszCurStr, [g_szzNames xWrtRIP]\n"
" lea xBX, [g_apfnImports xWrtRIP]\n"
".next_import:\n"
" cmp RTCCPTR_PRE [xBX], 0\n"
" je .return\n"
"%%ifdef ASM_CALL64_GCC\n"
" mov rdx, xBX ; ppvValue\n"
" mov rsi, pszCurStr ; pszSymbol\n"
" mov rdi, [g_hMod wrt rip] ; hLdrMod\n"
" call IMP2(RTLdrGetSymbol)\n"
"%%elifdef ASM_CALL64_MSC\n"
" mov r8, xBX ; ppvValue\n"
" mov rdx, pszCurStr ; pszSymbol\n"
" mov rcx, [g_hMod wrt rip] ; pszSymbol\n"
" call IMP2(RTLdrGetSymbol)\n"
"%%else\n"
" push xBX ; ppvValue\n"
" push pszCurStr ; pszSymbol\n"
" push RTCCPTR_PRE [g_hMod] ; hLdrMod\n"
" call IMP2(RTLdrGetSymbol)\n"
" add xSP, 0ch\n"
"%%endif\n"
" or eax, eax\n"
" jnz .symbol_error\n"
"\n"
" ; Advance.\n"
" add xBX, RTCCPTR_CB\n"
" xor eax, eax\n"
" mov xCX, 0ffffffffh\n"
"%%ifdef ASM_CALL64_GCC\n"
" mov xDI, pszCurStr\n"
" repne scasb\n"
" mov pszCurStr, xDI\n"
"%%else\n"
" repne scasb\n"
"%%endif\n"
" jmp .next_import\n"
"\n"
" ;\n"
" ; Error loading a symbol. Call RTErrInfoSet on pErrInfo (preserves eax).\n"
" ;\n"
".symbol_error:\n"
"%%ifdef ASM_CALL64_GCC\n"
" mov rdx, pszCurStr ; pszMsg\n"
" mov esi, eax ; rc\n"
" mov rdi, [xBP - xCB * 4] ; pErrInfo\n"
" call IMP2(RTErrInfoSet)\n"
"%%elifdef ASM_CALL64_MSC\n"
" mov r8, pszCurStr ; pszMsg\n"
" mov edx, eax ; rc\n"
" mov rcx, [xBP - xCB * 4] ; pErrInfo\n"
" call IMP2(RTErrInfoSet)\n"
"%%else\n"
" push pszCurStr ; pszMsg\n"
" push eax ; pszSymbol\n"
" push dword [xBP + 0ch] ; pErrInfo\n"
" call IMP2(RTErrInfoSet)\n"
" add xSP, 0ch\n"
"%%endif\n"
" "
"\n"
".return:\n"
" mov pszCurStr, [xBP - xCB * 2]\n"
" mov xBX, [xBP - xCB * 1]\n"
" leave\n"
" ret\n"
"ENDPROC ExplicitlyLoad%.*s\n"
"\n"
"\n"
,
);
}
return RTEXITCODE_SUCCESS;
}
/**
* Generates the assembly source code, writing it to g_pszOutput.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
* details has been displayed.
*/
static RTEXITCODE generateOutput(void)
{
if (pOutput)
{
{
}
}
else
return rcExit;
}
/**
* Displays usage information.
*
* @returns RTEXITCODE_SUCCESS.
* @param pszArgv0 The argv[0] string.
*/
{
printf("usage: %s [options] --libary <loadname> --output <lazyload.asm> <input.def>\n"
"\n"
"Options:\n"
" --explicit-load-function, --no-explicit-load-function\n"
" Whether to include the explicit load function, default is not to.\n"
"\n"
"Copyright (C) 2013-2015 Oracle Corporation\n"
, pszArgv0);
return RTEXITCODE_SUCCESS;
}
{
/*
* Parse options.
*/
for (int i = 1; i < argc; i++)
{
if (*psz == '-')
{
{
if (++i >= argc)
{
return RTEXITCODE_SYNTAX;
}
g_pszOutput = argv[i];
}
{
if (++i >= argc)
{
return RTEXITCODE_SYNTAX;
}
g_pszLibrary = argv[i];
}
g_fWithExplictLoadFunction = true;
g_fWithExplictLoadFunction = false;
/** @todo Support different load methods so this can be used on system libs and
* such if we like. */
{
printf("$Revision$\n");
return RTEXITCODE_SUCCESS;
}
else
{
return RTEXITCODE_SYNTAX;
}
}
else
{
{
fprintf(stderr, "syntax error: Too many input files, max is %d.\n", (int)RT_ELEMENTS(g_apszInputs));
return RTEXITCODE_SYNTAX;
}
}
}
if (g_cInputs == 0)
{
return RTEXITCODE_SYNTAX;
}
if (!g_pszOutput)
{
return RTEXITCODE_SYNTAX;
}
if (!g_pszLibrary)
{
return RTEXITCODE_SYNTAX;
}
/*
* Do the job.
*/
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = generateOutput();
return rcExit;
}