VBoxDef2LazyLoad.cpp revision 650460655986ed42a03aedf7b008c656291af279
/* $Id$ */
/** @file
* VBoxDef2LazyLoad - Lazy Library Loader Generator.
*
* @note Only tested on win.amd64.
*/
/*
* Copyright (C) 2013 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_pszInput = NULL;
static const char *g_pszLibrary = NULL;
static bool g_fIgnoreData = true;
/** @} */
/** 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 g_pszInput, 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 parseInput(void)
{
if (pInput)
{
{
}
}
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).
*/
{
";; Autogenerated from '%s'. DO NOT EDIT!\n"
"\n"
"%%include \"iprt/asmdefs.mac\"\n"
"\n"
"BEGINCODE\n",
{
"BEGINDATA\n"
"%%ifdef ASM_FORMAT_PE\n"
"global __imp_%s\n"
"__imp_%s:\n"
"%%endif\n"
"g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n"
"BEGINCODE\n"
"BEGINPROC %s\n"
" jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"
"ENDPROC %s\n"
"___LazyLoad___%s:\n"
/* "int3\n" */
"%%ifdef RT_ARCH_AMD64\n"
" lea rax, [.szName wrt rip]\n"
" lea r10, [g_pfn%s wrt rip]\n"
"%%elifdef RT_ARCH_X86\n"
" push .szName\n"
" push g_pfn%s\n"
"%%else\n"
" %%error \"Unsupported architecture\"\n"
"%%endif\n"
" call NAME(LazyLoadResolver)\n"
"%%ifdef RT_ARCH_X86\n"
" add esp, 8h\n"
"%%endif\n"
" jmp NAME(%s)\n"
".szName db '%s',0\n"
"\n"
,
}
/*
* The code that does the loading and resolving.
*/
"BEGINDATA\n"
"g_hMod RTCCPTR_DEF 0\n"
"\n"
"BEGINCODE\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 IMPNAME(SUPR3HardenedLdrLoadAppPriv)\n"
"\n"
"BEGINPROC 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 (local load)\n"
" xor rdx, rdx ; fFlags (local load)\n"
" lea rsi, [g_hMod wrt rip] ; phLdrMod\n"
" lea rdi, [.szLib 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 (local load)\n"
" xor r8, r8 ; fFlags (local load)\n"
" lea rdx, [g_hMod wrt rip] ; phLdrMod\n"
" lea rcx, [.szLib wrt rip] ; pszFilename\n"
" sub rsp, 28h\n"
" call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
" add rsp, 28h\n"
"\n"
"%%elifdef RT_ARCH_X86\n"
" sub rsp, 0ch\n"
" push 0 ; pErrInfo\n"
" push 0 ; fFlags (local load)\n"
" push g_hMod ; phLdrMod\n"
" push .szLib ; 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"
".szLib db '%s',0\n"
"ENDPROC LazyLoading\n"
, g_pszLibrary);
"\n"
";\n"
";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n"
";\n"
"extern IMPNAME(RTLdrGetSymbol)\n"
"BEGINPROC 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 NAME(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"
".badsym:\n"
" jz .symok\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 NAME(LazyLoading) ; returns handle in ecx\n"
" mov ecx, [g_hMod]\n"
" call IMP2(RTLdrGetSymbol)\n"
" or eax, eax\n"
".badsym:\n"
" jz .symok\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"
"ENDPROC LazyLoadResolver\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 --libary <loadname> --output <lazyload.asm> <input.def>\n"
"\n"
"Copyright (C) 2013 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];
}
/** @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
{
if (g_pszInput)
{
return RTEXITCODE_SYNTAX;
}
g_pszInput = argv[i];
}
}
if (!g_pszInput)
{
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;
}