diff options
Diffstat (limited to 'src/bldprogs/VBoxDef2LazyLoad.cpp')
-rw-r--r-- | src/bldprogs/VBoxDef2LazyLoad.cpp | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/src/bldprogs/VBoxDef2LazyLoad.cpp b/src/bldprogs/VBoxDef2LazyLoad.cpp new file mode 100644 index 00000000..fe0006a5 --- /dev/null +++ b/src/bldprogs/VBoxDef2LazyLoad.cpp @@ -0,0 +1,635 @@ +/* $Id: VBoxDef2LazyLoad.cpp $ */ +/** @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; + * you can redistribute it and/or modify it under the terms of the GNU + * 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> +#include <iprt/types.h> + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +typedef struct MYEXPORT +{ + struct MYEXPORT *pNext; + bool fNoName; + unsigned uOrdinal; + char szName[1]; +} MYEXPORT; +typedef MYEXPORT *PMYEXPORT; + + + +/******************************************************************************* +* 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. */ +static PMYEXPORT g_pExpHead = NULL; +/** Pointer to the next pointer for insertion. */ +static PMYEXPORT *g_ppExpNext = &g_pExpHead; + + + +static const char *leftStrip(const char *psz) +{ + while (isspace(*psz)) + psz++; + return psz; +} + + +static char *leftStrip(char *psz) +{ + while (isspace(*psz)) + psz++; + return psz; +} + + +static unsigned wordLength(const char *pszWord) +{ + unsigned off = 0; + char ch; + while ( (ch = pszWord[off]) != '\0' + && ch != '=' + && ch != ',' + && ch != ':' + && !isspace(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. + */ +static RTEXITCODE parseInputInner(FILE *pInput) +{ + /* + * Process the file line-by-line. + */ + bool fInExports = false; + unsigned iLine = 0; + char szLine[16384]; + while (fgets(szLine, sizeof(szLine), pInput)) + { + iLine++; + + /* + * Strip leading and trailing spaces from the line as well as + * trailing comments. + */ + char *psz = leftStrip(szLine); + if (*psz == ';') + continue; /* comment line. */ + + char *pszComment = strchr(psz, ';'); + if (pszComment) + *pszComment = '\0'; + + unsigned cch = (unsigned)strlen(psz); + while (cch > 0 && (isspace(psz[cch - 1]) || psz[cch - 1] == '\r' || psz[cch - 1] == '\n')) + psz[--cch] = '\0'; + + if (!cch) + continue; + + /* + * Check for known directives. + */ + size_t cchWord0 = wordLength(psz); +#define WORD_CMP(pszWord1, cchWord1, szWord2) \ + ( (cchWord1) == sizeof(szWord2) - 1 && memcmp(pszWord1, szWord2, sizeof(szWord2) - 1) == 0 ) + if (WORD_CMP(psz, cchWord0, "EXPORTS")) + { + fInExports = true; + + /* In case there is an export on the same line. (Really allowed?) */ + psz = leftStrip(psz + sizeof("EXPORTS") - 1); + 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. */ + else if ( WORD_CMP(psz, cchWord0, "NAME") + || WORD_CMP(psz, cchWord0, "LIBRARY") + || WORD_CMP(psz, cchWord0, "DESCRIPTION") + || WORD_CMP(psz, cchWord0, "STACKSIZE") + || WORD_CMP(psz, cchWord0, "SECTIONS") + || WORD_CMP(psz, cchWord0, "SEGMENTS") + || WORD_CMP(psz, cchWord0, "VERSION") + ) + { + fInExports = false; + } + + /* + * Process exports: + * entryname[=internalname] [@ordinal[ ][NONAME]] [DATA] [PRIVATE] + */ + if (fInExports) + { + const char *pchName = psz; + unsigned cchName = wordLength(psz); + + psz = leftStrip(psz + cchName); + if (*psz == '=') + { + psz = leftStrip(psz + 1); + psz = leftStrip(psz + wordLength(psz)); + } + + bool fNoName = true; + unsigned uOrdinal = ~0U; + if (*psz == '@') + { + psz++; + if (!isdigit(*psz)) + { + fprintf(stderr, "%s:%u: error: Invalid ordinal spec.\n", g_pszInput, iLine); + return RTEXITCODE_FAILURE; + } + uOrdinal = *psz++ - '0'; + while (isdigit(*psz)) + { + uOrdinal *= 10; + uOrdinal += *psz++ - '0'; + } + psz = leftStrip(psz); + cch = wordLength(psz); + if (WORD_CMP(psz, cch, "NONAME")) + { +#if 0 + fNoName = true; + psz = leftStrip(psz + cch); +#else + fprintf(stderr, "%s:%u: error: NONAME export not implemented.\n", g_pszInput, iLine); + return RTEXITCODE_FAILURE; +#endif + } + } + + while (*psz) + { + cch = wordLength(psz); + if (WORD_CMP(psz, cch, "DATA")) + { + if (!g_fIgnoreData) + { + fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n", + g_pszInput, iLine, cchName, pchName); + return RTEXITCODE_SUCCESS; + } + } + else if (!WORD_CMP(psz, cch, "PRIVATE")) + { + fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n", + g_pszInput, iLine, cchName, pchName); + return RTEXITCODE_SUCCESS; + } + psz = leftStrip(psz + cch); + } + + /* + * Add the export. + */ + PMYEXPORT pExp = (PMYEXPORT)malloc(sizeof(*pExp) + cchName); + if (!pExp) + { + fprintf(stderr, "%s:%u: error: Out of memory.\n", g_pszInput, iLine); + return RTEXITCODE_SUCCESS; + } + memcpy(pExp->szName, pchName, cchName); + pExp->szName[cchName] = '\0'; + pExp->uOrdinal = uOrdinal; + pExp->fNoName = fNoName; + pExp->pNext = NULL; + *g_ppExpNext = pExp; + g_ppExpNext = &pExp->pNext; + } + } + + /* + * Why did we quit the loop, EOF or error? + */ + if (feof(pInput)) + return RTEXITCODE_SUCCESS; + fprintf(stderr, "error: Read while reading '%s' (iLine=%u).\n", g_pszInput, iLine); + 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) +{ + RTEXITCODE rcExit = RTEXITCODE_FAILURE; + FILE *pInput = fopen(g_pszInput, "r"); + if (pInput) + { + rcExit = parseInputInner(pInput); + fclose(pInput); + if (rcExit == RTEXITCODE_SUCCESS && !g_pExpHead) + { + fprintf(stderr, "error: Found no exports in '%s'.\n", g_pszInput); + rcExit = RTEXITCODE_FAILURE; + } + } + else + fprintf(stderr, "error: Failed to open '%s' for reading.\n", g_pszInput); + 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). + */ +static RTEXITCODE generateOutputInner(FILE *pOutput) +{ + fprintf(pOutput, + ";; Autogenerated from '%s'. DO NOT EDIT!\n" + "\n" + "%%include \"iprt/asmdefs.mac\"\n" + "\n" + "BEGINCODE\n", + g_pszInput); + + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + { + fprintf(pOutput, + "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" + , + pExp->szName, + pExp->szName, + pExp->szName, pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName, + pExp->szName); + } + + /* + * The code that does the loading and resolving. + */ + fprintf(pOutput, + "BEGINDATA\n" + "g_hMod RTCCPTR_DEF 0\n" + "\n" + "BEGINCODE\n"); + + /* + * How we load the module needs to be selectable later on. + * + * The LazyLoading routine returns the module handle in RCX/ECX, caller + * saved all necessary registers. + */ + fprintf(pOutput, + ";\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); + + + fprintf(pOutput, + "\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) +{ + RTEXITCODE rcExit = RTEXITCODE_FAILURE; + FILE *pOutput = fopen(g_pszOutput, "w"); + if (pOutput) + { + rcExit = generateOutputInner(pOutput); + if (fclose(pOutput)) + { + fprintf(stderr, "error: Error closing '%s'.\n", g_pszOutput); + rcExit = RTEXITCODE_FAILURE; + } + } + else + fprintf(stderr, "error: Failed to open '%s' for writing.\n", g_pszOutput); + return rcExit; +} + + +/** + * Displays usage information. + * + * @returns RTEXITCODE_SUCCESS. + * @param pszArgv0 The argv[0] string. + */ +static int usage(const char *pszArgv0) +{ + printf("usage: %s --libary <loadname> --output <lazyload.asm> <input.def>\n" + "\n" + "Copyright (C) 2013 Oracle Corporation\n" + , pszArgv0); + + return RTEXITCODE_SUCCESS; +} + + +int main(int argc, char **argv) +{ + /* + * Parse options. + */ + for (int i = 1; i < argc; i++) + { + const char *psz = argv[i]; + if (*psz == '-') + { + if (!strcmp(psz, "--output") || !strcmp(psz, "-o")) + { + if (++i >= argc) + { + fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz); + return RTEXITCODE_SYNTAX; + } + g_pszOutput = argv[i]; + } + else if (!strcmp(psz, "--library") || !strcmp(psz, "-l")) + { + if (++i >= argc) + { + fprintf(stderr, "syntax error: Library name expected after '%s'.\n", psz); + 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. */ + else if ( !strcmp(psz, "--help") + || !strcmp(psz, "-help") + || !strcmp(psz, "-h") + || !strcmp(psz, "-?") ) + return usage(argv[0]); + else if ( !strcmp(psz, "--version") + || !strcmp(psz, "-V")) + { + printf("$Revision: 87893 $\n"); + return RTEXITCODE_SUCCESS; + } + else + { + fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz); + return RTEXITCODE_SYNTAX; + } + } + else + { + if (g_pszInput) + { + fprintf(stderr, "syntax error: Already specified '%s' as the input file.\n", g_pszInput); + return RTEXITCODE_SYNTAX; + } + g_pszInput = argv[i]; + } + } + if (!g_pszInput) + { + fprintf(stderr, "syntax error: No input file specified.\n"); + return RTEXITCODE_SYNTAX; + } + if (!g_pszOutput) + { + fprintf(stderr, "syntax error: No output file specified.\n"); + return RTEXITCODE_SYNTAX; + } + if (!g_pszLibrary) + { + fprintf(stderr, "syntax error: No library name specified.\n"); + return RTEXITCODE_SYNTAX; + } + + /* + * Do the job. + */ + RTEXITCODE rcExit = parseInput(); + if (rcExit == RTEXITCODE_SUCCESS) + rcExit = generateOutput(); + return rcExit; +} + |