diff options
Diffstat (limited to 'src/bldprogs/VBoxDef2LazyLoad.cpp')
-rw-r--r-- | src/bldprogs/VBoxDef2LazyLoad.cpp | 1624 |
1 files changed, 1624 insertions, 0 deletions
diff --git a/src/bldprogs/VBoxDef2LazyLoad.cpp b/src/bldprogs/VBoxDef2LazyLoad.cpp new file mode 100644 index 00000000..8cd9b44e --- /dev/null +++ b/src/bldprogs/VBoxDef2LazyLoad.cpp @@ -0,0 +1,1624 @@ +/* $Id: VBoxDef2LazyLoad.cpp $ */ +/** @file + * VBoxDef2LazyLoad - Lazy Library Loader Generator. + * + * @note Only tested on win.amd64 & darwin.amd64. + */ + +/* + * Copyright (C) 2013-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <iprt/types.h> +#include <iprt/ldr.h> /* For RTLDRARCH. */ + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct MYEXPORT +{ + struct MYEXPORT *pNext; + /** Pointer to unmangled name for stdcall (after szName), NULL if not. */ + char *pszUnstdcallName; + /** Pointer to the exported name. */ + char const *pszExportedNm; + unsigned uOrdinal; + bool fNoName; + char szName[1]; +} MYEXPORT; +typedef MYEXPORT *PMYEXPORT; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** @name Options + * @{ */ +static const char *g_pszOutput = NULL; +static const char *g_pszLibrary = NULL; +static const char *g_apszInputs[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +static unsigned g_cInputs = 0; +static bool g_fIgnoreData = true; +static bool g_fWithExplictLoadFunction = false; +static bool g_fSystemLibrary = false; +#if defined(RT_ARCH_AMD64) +static RTLDRARCH g_enmTarget = RTLDRARCH_AMD64; +#elif defined(RT_ARCH_X86) +static RTLDRARCH g_enmTarget = RTLDRARCH_X86_32; +#elif defined(RT_ARCH_ARM64) +static RTLDRARCH g_enmTarget = RTLDRARCH_ARM64; +#else +# error "Port me!" +#endif +/** @} */ + +/** 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; + + + +#if 0 /* unused */ +static const char *leftStrip(const char *psz) +{ + while (isspace(*psz)) + psz++; + return psz; +} +#endif + + +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, const char *pszInput) +{ + /* + * 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 = false; + unsigned uOrdinal = ~0U; + if (*psz == '@') + { + psz++; + if (!isdigit(*psz)) + { + fprintf(stderr, "%s:%u: error: Invalid ordinal spec.\n", 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")) + { + fNoName = true; + psz = leftStrip(psz + cch); + } + } + + 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", + 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", + pszInput, iLine, cchName, pchName); + return RTEXITCODE_SUCCESS; + } + psz = leftStrip(psz + cch); + } + + /* + * Check for stdcall mangling. + */ + size_t cbExp = sizeof(MYEXPORT) + cchName; + unsigned cchStdcall = 0; + if (cchName > 3 && *pchName == '_' && isdigit(pchName[cchName - 1])) + { + if (cchName > 3 && pchName[cchName - 2] == '@') + cchStdcall = 2; + else if (cchName > 4 && pchName[cchName - 3] == '@' && isdigit(pchName[cchName - 2])) + cchStdcall = 3; + if (cchStdcall) + cbExp += cchName - 1 - cchStdcall; + } + + /* + * Add the export. + */ + + PMYEXPORT pExp = (PMYEXPORT)malloc(cbExp); + if (!pExp) + { + fprintf(stderr, "%s:%u: error: Out of memory.\n", pszInput, iLine); + return RTEXITCODE_SUCCESS; + } + memcpy(pExp->szName, pchName, cchName); + pExp->szName[cchName] = '\0'; + if (!cchStdcall) + { + pExp->pszUnstdcallName = NULL; + pExp->pszExportedNm = pExp->szName; + } + else + { + pExp->pszUnstdcallName = &pExp->szName[cchName + 1]; + memcpy(pExp->pszUnstdcallName, pchName + 1, cchName - 1 - cchStdcall); + pExp->pszUnstdcallName[cchName - 1 - cchStdcall] = '\0'; + pExp->pszExportedNm = pExp->pszUnstdcallName; + } + 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", pszInput, iLine); + 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) +{ + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + for (unsigned i = 0; i < g_cInputs; i++) + { + FILE *pInput = fopen(g_apszInputs[i], "r"); + if (pInput) + { + RTEXITCODE rcExit2 = parseInputInner(pInput, g_apszInputs[i]); + fclose(pInput); + if (rcExit2 == RTEXITCODE_SUCCESS && !g_pExpHead) + { + fprintf(stderr, "error: Found no exports in '%s'.\n", g_apszInputs[i]); + rcExit2 = RTEXITCODE_FAILURE; + } + if (rcExit2 != RTEXITCODE_SUCCESS) + rcExit = rcExit2; + } + else + { + fprintf(stderr, "error: Failed to open '%s' for reading.\n", g_apszInputs[i]); + rcExit = RTEXITCODE_FAILURE; + } + } + return rcExit; +} + + +/** + * Generates the assembly source code for AMD64 and x86, 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 generateOutputInnerX86AndAMD64(FILE *pOutput) +{ + fprintf(pOutput, ";;\n"); + for (unsigned i = 0; i < g_cInputs; i++) + fprintf(pOutput, ";; Autogenerated from '%s'.\n", g_apszInputs[i]); + + fprintf(pOutput, + ";; 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. + */ + fprintf(pOutput, + ";\n" + "; Thunks.\n" + ";\n" + "BEGINCODE\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + if (!pExp->pszUnstdcallName) + fprintf(pOutput, + "BEGINPROC %s\n" + " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n" + "ENDPROC %s\n", + pExp->szName, pExp->szName, pExp->szName); + else + fprintf(pOutput, + "%%ifdef RT_ARCH_X86\n" + "global %s\n" + "%s:\n" + " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n" + "%%else\n" + "BEGINPROC %s\n" + " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n" + "ENDPROC %s\n" + "%%endif\n", + pExp->szName, pExp->szName, pExp->pszUnstdcallName, + pExp->pszUnstdcallName, pExp->pszUnstdcallName, pExp->pszUnstdcallName); + + fprintf(pOutput, + "\n" + "\n"); + + /* + * Import pointers + */ + fprintf(pOutput, + ";\n" + "; Import pointers. Initialized to point a lazy loading stubs.\n" + ";\n" + "BEGINDATA\n" + "g_apfnImports:\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + if (pExp->pszUnstdcallName) + fprintf(pOutput, + "%%ifdef ASM_FORMAT_PE\n" + " %%ifdef RT_ARCH_X86\n" + "global __imp_%s\n" + "__imp_%s:\n" + " %%else\n" + "global __imp_%s\n" + "__imp_%s:\n" + " %%endif\n" + "%%endif\n" + "g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n" + "\n", + pExp->szName, + pExp->szName, + pExp->pszUnstdcallName, + pExp->pszUnstdcallName, + pExp->pszExportedNm, + pExp->pszExportedNm); + else + fprintf(pOutput, + "%%ifdef ASM_FORMAT_PE\n" + "global __imp_%s\n" + "__imp_%s:\n" + "%%endif\n" + "g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n" + "\n", + pExp->szName, + pExp->szName, + pExp->pszExportedNm, + pExp->pszExportedNm); + fprintf(pOutput, + "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. + */ + fprintf(pOutput, + ";\n" + "; Imported names.\n" + ";\n" + "BEGINCODE\n" + "g_szLibrary: db '%s',0\n" + "\n" + "g_szzNames:\n", + g_pszLibrary); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + if (!pExp->fNoName) + fprintf(pOutput, " g_sz%s:\n db '%s',0\n", pExp->pszExportedNm, pExp->pszExportedNm); + else + fprintf(pOutput, " g_sz%s:\n db '#%u',0\n", pExp->pszExportedNm, pExp->uOrdinal); + fprintf(pOutput, + "g_EndOfNames: db 0\n" + "\n" + "g_szFailLoadFmt: db 'Lazy loader failed to load \"%%s\": %%Rrc', 10, 0\n" + "g_szFailResolveFmt: db 'Lazy loader failed to resolve symbol \"%%s\" in \"%%s\": %%Rrc', 10, 0\n" + "\n" + "\n"); + + /* + * The per import lazy load code. + */ + fprintf(pOutput, + ";\n" + "; Lazy load+resolve stubs.\n" + ";\n" + "BEGINCODE\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + { + if (!pExp->fNoName) + fprintf(pOutput, + "___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" + " call LazyLoadResolver\n" + "%%elifdef RT_ARCH_X86\n" + " push g_sz%s\n" + " push g_pfn%s\n" + " call LazyLoadResolver\n" + " add esp, 8h\n" + "%%else\n" + " %%error \"Unsupported architecture\"\n" + "%%endif\n" + , + pExp->pszExportedNm, + pExp->pszExportedNm, + pExp->pszExportedNm, + pExp->pszExportedNm, + pExp->pszExportedNm); + else + fprintf(pOutput, + "___LazyLoad___%s:\n" + /* "int3\n" */ + "%%ifdef RT_ARCH_AMD64\n" + " mov eax, %u\n" + " lea r10, [g_pfn%s wrt rip]\n" + " call LazyLoadResolver\n" + "%%elifdef RT_ARCH_X86\n" + " push %u\n" + " push g_pfn%s\n" + " call LazyLoadResolver\n" + " add esp, 8h\n" + "%%else\n" + " %%error \"Unsupported architecture\"\n" + "%%endif\n" + , + pExp->pszExportedNm, + pExp->uOrdinal, + pExp->pszExportedNm, + pExp->uOrdinal, + pExp->pszExportedNm); + if (!pExp->pszUnstdcallName) + fprintf(pOutput, " jmp NAME(%s)\n", pExp->szName); + else + fprintf(pOutput, + "%%ifdef RT_ARCH_X86\n" + " jmp %s\n" + "%%else\n" + " jmp NAME(%s)\n" + "%%endif\n" + , + pExp->szName, pExp->pszUnstdcallName); + fprintf(pOutput, "\n"); + } + fprintf(pOutput, + "\n" + "\n" + "\n"); + + /* + * The code that does the loading and resolving. + */ + fprintf(pOutput, + ";\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. + * + * The LazyLoading routine returns the module handle in RCX/ECX, caller + * saved all necessary registers. + */ + if (!g_fSystemLibrary) + fprintf(pOutput, + ";\n" + ";SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod,\n" + "; uint32_t fFlags, PRTERRINFO pErrInfo);\n" + ";\n" + "EXTERN_IMP2 SUPR3HardenedLdrLoadAppPriv\n" + "%%ifdef IN_RT_R3\n" + "extern NAME(RTAssertMsg2Weak)\n" + "%%else\n" + "EXTERN_IMP2 RTAssertMsg2Weak\n" + "%%endif\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"); + else + fprintf(pOutput, + ";\n" + "; RTDECL(int) RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod);\n" + ";\n" + "%%ifdef IN_RT_R3\n" + "extern NAME(RTLdrLoadSystem)\n" + "extern NAME(RTAssertMsg2Weak)\n" + "%%else\n" + "EXTERN_IMP2 RTLdrLoadSystem\n" + "EXTERN_IMP2 RTAssertMsg2Weak\n" + "%%endif\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" + " lea rdx, [g_hMod wrt rip] ; phLdrMod\n" + " mov esi, 1 ; fNoUnload=true\n" + " lea rdi, [g_szLibrary wrt rip] ; pszFilename\n" + " sub rsp, 08h\n" + " %%ifdef IN_RT_R3\n" + " call NAME(RTLdrLoadSystem)\n" + " %%else\n" + " call IMP2(RTLdrLoadSystem)\n" + " %%endif\n" + " add rsp, 08h\n" + "\n" + "%%elifdef ASM_CALL64_MSC\n" + " lea r8, [g_hMod wrt rip] ; phLdrMod\n" + " mov edx, 1 ; fNoUnload=true\n" + " lea rcx, [g_szLibrary wrt rip] ; pszFilename\n" + " sub rsp, 28h\n" + " %%ifdef IN_RT_R3\n" + " call NAME(RTLdrLoadSystem)\n" + " %%else\n" + " call IMP2(RTLdrLoadSystem)\n" + " %%endif\n" + " add rsp, 28h\n" + "\n" + "%%elifdef RT_ARCH_X86\n" + " push g_hMod ; phLdrMod\n" + " push 1 ; fNoUnload=true\n" + " push g_szLibrary ; pszFilename\n" + " %%ifdef IN_RT_R3\n" + " call NAME(RTLdrLoadSystem)\n" + " %%else\n" + " call IMP2(RTLdrLoadSystem)\n" + " %%endif\n" + " add esp, 0ch\n" + "%%else\n" + " %%error \"Unsupported architecture\"\n" + "%%endif\n"); + fprintf(pOutput, + " or eax, eax\n" + " jnz .badload\n" + " mov xCX, [g_hMod xWrtRIP]\n" + ".return:\n" + " ret\n" + "\n" + ".badload:\n" + "%%ifdef ASM_CALL64_GCC\n" + " mov edx, eax\n" + " lea rsi, [g_szLibrary wrt rip]\n" + " lea rdi, [g_szFailLoadFmt wrt rip]\n" + " sub rsp, 08h\n" + "%%elifdef ASM_CALL64_MSC\n" + " mov r8d, eax\n" + " lea rdx, [g_szLibrary wrt rip]\n" + " lea rcx, [g_szFailLoadFmt wrt rip]\n" + " sub rsp, 28h\n" + "%%elifdef RT_ARCH_X86\n" + " push eax\n" + " push g_szLibrary\n" + " push g_szFailLoadFmt\n" + "%%endif\n" + "%%ifdef IN_RT_R3\n" + " call NAME(RTAssertMsg2Weak)\n" + "%%else\n" + " call IMP2(RTAssertMsg2Weak)\n" + "%%endif\n" + ".badloadloop:\n" + " int3\n" + " jmp .badloadloop\n" + "LazyLoading_End:\n" + "\n" + "\n"); + + + fprintf(pOutput, + ";\n" + ";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n" + ";\n" + "%%ifdef IN_RT_R3\n" + "extern NAME(RTLdrGetSymbol)\n" + "%%else\n" + "EXTERN_IMP2 RTLdrGetSymbol\n" + "%%endif\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" + " %%ifdef IN_RT_R3\n" + " call NAME(RTLdrGetSymbol)\n" + " %%else\n" + " call IMP2(RTLdrGetSymbol)\n" + " %%endif\n" + " or eax, eax\n" + " jnz .badsym\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" + " call LazyLoading ; returns handle in ecx\n" + " push dword [ebp + 8] ; value addr\n" + " push dword [ebp + 12] ; symbol name\n" + " push ecx\n" + " %%ifdef IN_RT_R3\n" + " call NAME(RTLdrGetSymbol)\n" + " %%else\n" + " call IMP2(RTLdrGetSymbol)\n" + " %%endif\n" + " or eax, eax\n" + " jnz .badsym\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" + "\n" + ".badsym:\n" + "%%ifdef ASM_CALL64_GCC\n" + " mov ecx, eax\n" + " lea rdx, [g_szLibrary wrt rip]\n" + " mov rsi, r15\n" + " lea rdi, [g_szFailResolveFmt wrt rip]\n" + " sub rsp, 08h\n" + "%%elifdef ASM_CALL64_MSC\n" + " mov r9d, eax\n" + " mov r8, r15\n" + " lea rdx, [g_szLibrary wrt rip]\n" + " lea rcx, [g_szFailResolveFmt wrt rip]\n" + " sub rsp, 28h\n" + "%%elifdef RT_ARCH_X86\n" + " push eax\n" + " push dword [ebp + 12]\n" + " push g_szLibrary\n" + " push g_szFailResolveFmt\n" + "%%endif\n" + "%%ifdef IN_RT_R3\n" + " call NAME(RTAssertMsg2Weak)\n" + "%%else\n" + " call IMP2(RTAssertMsg2Weak)\n" + "%%endif\n" + ".badsymloop:\n" + " int3\n" + " jmp .badsymloop\n" + "\n" + "LazyLoadResolver_End:\n" + "\n" + "\n" + ); + + + + /* + * C callable method for explicitly loading the library and optionally + * resolving all the imports. + */ + if (g_fWithExplictLoadFunction) + { + if (g_fSystemLibrary) /* Lazy bird. */ + { + fprintf(stderr, "error: cannot use --system with --explicit-load-function, sorry\n"); + return RTEXITCODE_FAILURE; + } + + int cchLibBaseName = (int)(strchr(g_pszLibrary, '.') ? strchr(g_pszLibrary, '.') - g_pszLibrary : strlen(g_pszLibrary)); + fprintf(pOutput, + ";;\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" + , + cchLibBaseName, g_pszLibrary, + cchLibBaseName, g_pszLibrary, + cchLibBaseName, g_pszLibrary + ); + } + + + return RTEXITCODE_SUCCESS; +} + + +/** + * Generates the assembly source code for ARM64, 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 generateOutputInnerArm64(FILE *pOutput) +{ +// bool fMachO = true; +// bool fDarwin = true; + const char *pszNmPfx = "_"; + + fprintf(pOutput, ";;\n"); + for (unsigned i = 0; i < g_cInputs; i++) + fprintf(pOutput, ";; Autogenerated from '%s'.\n", g_apszInputs[i]); + + fprintf(pOutput, + ";; 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. + */ + fprintf(pOutput, + ";\n" + "; Thunks.\n" + ";\n" + ".section __TEXT,__text,regular,pure_instructions\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + fprintf(pOutput, + ".p2align 3\n" + ".globl %s%s\n" + "%s%s:\n" + " adrp x9, %sg_pfn%s@PAGE\n" + " ldr x9, [x9, %sg_pfn%s@PAGEOFF]\n" + " br x9\n", + pszNmPfx, pExp->szName, pszNmPfx, pExp->szName, pszNmPfx, pExp->szName, pszNmPfx, pExp->szName); + fprintf(pOutput, + "\n" + "\n"); + + /* + * Import pointers + */ + fprintf(pOutput, + ";\n" + "; Import pointers. Initialized to point a lazy loading stubs.\n" + ";\n" + ".section __DATA,__data\n" + ".p2align 3\n" + "g_apfnImports:\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + fprintf(pOutput, + ".globl __imp_%s\n" + "__imp_%s:\n" + ".globl %sg_pfn%s\n" + "%sg_pfn%s:\n" + " .quad ___LazyLoad___%s\n" + "\n", + pExp->szName, pExp->szName, + pszNmPfx, pExp->szName, pszNmPfx, pExp->szName, + pExp->pszExportedNm); + fprintf(pOutput, + " .quad 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. + */ + fprintf(pOutput, + ";\n" + "; Imported names.\n" + ";\n" + ".section __TEXT,__cstring,cstring_literals\n" + "g_szLibrary:\n" + " .asciz \"%s\"\n" + "\n" + "g_szzNames:\n", + g_pszLibrary); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + if (!pExp->fNoName) + fprintf(pOutput, " g_sz%s:\n .asciz \"%s\"\n", pExp->pszExportedNm, pExp->pszExportedNm); + else + fprintf(pOutput, " g_sz%s:\n .asciz \"#%u\"\n", pExp->pszExportedNm, pExp->uOrdinal); + fprintf(pOutput, + "g_EndOfNames: .byte 0\n" + "\n" + "g_szFailLoadFmt: .asciz \"Lazy loader failed to load \\\"%%s\\\": %%Rrc\\n\"\n" + "g_szFailResolveFmt: .asciz \"Lazy loader failed to resolve symbol \\\"%%s\\\" in \\\"%%s\\\": %%Rrc\\n\"\n" + "\n" + "\n"); + + /* + * The per import lazy load code. + */ + fprintf(pOutput, + ";\n" + "; Lazy load+resolve stubs.\n" + ";\n" + ".section __TEXT,__text,regular,pure_instructions\n" + ".p2align 3\n"); + for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext) + { + if (!pExp->fNoName) + fprintf(pOutput, + "___LazyLoad___%s:\n" + " adrp x9, g_sz%s@PAGE\n" + " add x9, x9, g_sz%s@PAGEOFF\n" + " adrp x10, %sg_pfn%s@PAGE\n" + " add x10, x10, %sg_pfn%s@PAGEOFF\n" + " bl LazyLoadResolver\n" + , pExp->pszExportedNm, + pExp->pszExportedNm, pExp->pszExportedNm, + pszNmPfx, pExp->pszExportedNm, pszNmPfx, pExp->pszExportedNm); + else + fprintf(pOutput, + "___LazyLoad___%s:\n" + " movk w9, #%u\n" + " adrp x10, %sg_pfn%s@PAGE\n" + " add x10, x10, %sg_pfn%s@PAGEOFF\n" + , pExp->pszExportedNm, + pExp->uOrdinal, + pszNmPfx, pExp->pszExportedNm, pszNmPfx, pExp->pszExportedNm); + fprintf(pOutput, " b %s%s\n", pszNmPfx, pExp->szName); + fprintf(pOutput, "\n"); + } + fprintf(pOutput, + "\n" + "\n" + "\n"); + + /* + * The code that does the loading and resolving. + */ + fprintf(pOutput, + ";\n" + "; The module handle.\n" + ";\n" + ".section __DATA,__data\n" + "g_hMod:\n" + " .quad 0\n" + "\n" + "\n" + "\n"); + + /* + * Common lazy loader and resolved. + */ + fprintf(pOutput, + ";\n" + "; The resolver code.\n" + ";\n" + ".section __TEXT,__text,regular,pure_instructions\n" + ".p2align 3\n" + "LazyLoadResolver:\n" + " .cfi_startproc\n" + " ; Create frame.\n" + " sub sp, sp, #(16 + 192)\n" + " stp x29, x30, [sp, #192]\n" + " add x29, sp, #192\n" + " .cfi_def_cfa x29, 16\n" + " .cfi_offset x30, -8\n" + " .cfi_offset x29, -16\n" + " ; Save all argument registers and a handful of preserved ones.\n" + " stp x0, x1, [sp, #(192 - 16)]\n" + " .cfi_offset x0, -32\n" + " .cfi_offset x1, -24\n" + " stp x2, x3, [sp, #(192 - 32)]\n" + " .cfi_offset x3, -40\n" + " .cfi_offset x2, -48\n" + " stp x4, x5, [sp, #(192 - 48)]\n" + " .cfi_offset x6, -56\n" + " .cfi_offset x5, -64\n" + " stp x6, x7, [sp, #(192 - 64)]\n" + " .cfi_offset x7, -72\n" + " .cfi_offset x6, -80\n" + " stp x16, x17, [sp, #(192 - 80)]\n" + " .cfi_offset x17, -88\n" + " .cfi_offset x16, -96\n" + " stp x18, x19, [sp, #(192 - 96)]\n" + " .cfi_offset x19, -104\n" + " .cfi_offset x18, -112\n" + " stp x20, x21, [sp, #(192 - 112)]\n" + " .cfi_offset x21, -120\n" + " .cfi_offset x20, -128\n" + " stp x22, x23, [sp, #(192 - 128)]\n" + " .cfi_offset x23, -136\n" + " .cfi_offset x22, -144\n" + " str x8, [sp, #(192 - 144)]\n" + "\n" + " ; Shift the symbol name to x19 and g_pfnXXXX pointer to x20 as these are preserved registers\n" + " ; (in case we need to call LazyLoadModule/RTLdrLoad)\n" + " mov x19, x9\n" + " mov x20, x10\n" + "\n" + " ; Get the module handle and call RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue)\n" + " adrp x0, g_hMod@PAGE\n" + " ldr x0, [x0, g_hMod@PAGEOFF]\n" + " cmp x0, #0\n" + " b.eq LazyLoading\n" + " mov x1, x19\n" + " mov x2, x20\n" + " bl %sRTLdrGetSymbol\n" + "\n" + " cmp w0, #0\n" + " b.eq Lreturn\n" + "\n" + "Lbadsym: ; Call sRTAssertMsg2Weak. Variadic (...) arguments are passed on the stack it seems.\n" + " mov x3, x0\n" + " adrp x2, g_szLibrary@PAGE\n" + " add x2, x2, g_szLibrary@PAGEOFF\n" + " mov x1, x19\n" + " adrp x0, g_szFailLoadFmt@PAGE\n" + " add x0, x0, g_szFailLoadFmt@PAGEOFF\n" + " stp x1, x2, [sp]\n" + " str x3, [sp, #16]\n" + " bl %sRTAssertMsg2Weak\n" + "Lbadsymloop:\n" + " brk #0x1\n" + " b Lbadsymloop\n" + + "Lreturn:\n" + " ; Restore saved register\n" + " ldr x8, [sp, #(192 - 144)]\n" + " .cfi_restore x8\n" + " ldp x22, x23, [sp, #(192 - 128)]\n" + " .cfi_restore x23\n" + " .cfi_restore x22\n" + " ldp x20, x21, [sp, #(192 - 112)]\n" + " .cfi_restore x21\n" + " .cfi_restore x20\n" + " ldp x18, x19, [sp, #(192 - 96)]\n" + " .cfi_restore x19\n" + " .cfi_restore x18\n" + " ldp x16, x17, [sp, #(192 - 80)]\n" + " .cfi_restore x17\n" + " .cfi_restore x18\n" + " ldp x6, x7, [sp, #(192 - 64)]\n" + " .cfi_restore x7\n" + " .cfi_restore x6\n" + " ldp x4, x5, [sp, #(192 - 48)]\n" + " .cfi_restore x5\n" + " .cfi_restore x4\n" + " ldp x2, x3, [sp, #(192 - 32)]\n" + " .cfi_restore x3\n" + " .cfi_restore x2\n" + " ldp x0, x1, [sp, #(192 - 16)]\n" + " .cfi_restore x1\n" + " .cfi_restore x0\n" + "\n" + " ldp x29, x30, [sp, #192]\n" + " .cfi_restore x29\n" + " .cfi_restore x30\n" + " add sp, sp, #(16 + 192)\n" + " ret\n" + " .cfi_endproc\n" + "\n" + "\n" + , pszNmPfx, pszNmPfx); + + fprintf(pOutput, + ";\n" + "; Loads the module.\n" + "; ASSUMES called from LazyLoadResolver where all relevant registers are already saved.\n" + ";\n" + "LazyLoading:\n" + " .cfi_startproc\n" + " ; Create frame.\n" + " sub sp, sp, #(16 + 48)\n" + " stp x29, x30, [sp, #48]\n" + " add x29, sp, #48\n" + " .cfi_def_cfa x29, 16\n" + " .cfi_offset x30, -8\n" + " .cfi_offset x29, -16\n" + "\n"); + + if (!g_fSystemLibrary) + fprintf(pOutput, + " ; Call SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo);\n" + " mov x3, #0\n" + " mov x2, #0\n" + " adrp x1, g_hMod@PAGE\n" + " add x1, x1, g_hMod@PAGEOFF\n" + " adrp x0, g_szLibrary@PAGE\n" + " add x0, x0, g_szLibrary@PAGEOFF\n" + " bl %sSUPR3HardenedLdrLoadAppPriv\n" + , pszNmPfx); + else + fprintf(pOutput, + " ; Call RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod);\n" + " adrp x2, g_hMod@PAGE\n" + " add x2, x2, g_hMod@PAGEOFF\n" + " mov x1, #1\n" + " adrp x0, g_szLibrary@PAGE\n" + " add x0, x0, g_szLibrary@PAGEOFF\n" + " bl %sRTLdrLoadSystem\n" + , pszNmPfx); + + fprintf(pOutput, + " cmp w0, #0\n" + " b.eq Lload_return\n" + "\n" + "Lbadload: ; Call sRTAssertMsg2Weak. Variadic (...) arguments are passed on the stack it seems.\n" + " mov x2, x0\n" + " adrp x1, g_szLibrary@PAGE\n" + " add x1, x1, g_szLibrary@PAGEOFF\n" + " adrp x0, g_szFailResolveFmt@PAGE\n" + " add x0, x0, g_szFailResolveFmt@PAGEOFF\n" + " stp x1, x2, [sp]\n" + " bl %sRTAssertMsg2Weak\n" + "Lbadloadloop:\n" + " brk #0x1\n" + " b Lbadloadloop\n" + "Lload_return:\n" + " adrp x0, g_hMod@PAGE\n" + " ldr x0, [x0, g_hMod@PAGEOFF]\n" + " ldp x29, x30, [sp, #48]\n" + " .cfi_restore x29\n" + " .cfi_restore x30\n" + " add sp, sp, #(16 + 48)\n" + " ret\n" + " .cfi_endproc\n" + "\n" + "\n" + , pszNmPfx); + + /* + * C callable method for explicitly loading the library and optionally + * resolving all the imports. + */ + if (g_fWithExplictLoadFunction) + { + if (g_fSystemLibrary) /* Lazy bird. */ + { + fprintf(stderr, "error: cannot use --system with --explicit-load-function, sorry\n"); + return RTEXITCODE_FAILURE; + } + + int cchLibBaseName = (int)(strchr(g_pszLibrary, '.') ? strchr(g_pszLibrary, '.') - g_pszLibrary : strlen(g_pszLibrary)); + fprintf(pOutput, + ";;\n" + "; ExplicitlyLoad%.*s(bool fResolveAllImports, pErrInfo);\n" + ";\n" + ".section __TEXT,__text,regular,pure_instructions\n" + ".p2align 3\n" + ".globl %sExplicitlyLoad%.*s\n" + "%sExplicitlyLoad%.*s:\n" + " .cfi_startproc\n" + " ; Create frame.\n" + " sub sp, sp, #(16 + 96)\n" + " stp x29, x30, [sp, #96]\n" + " add x29, sp, #96\n" + " .cfi_def_cfa x29, 16\n" + " .cfi_offset x30, -8\n" + " .cfi_offset x29, -16\n" + "\n" + " stp x20, x21, [sp, #(96 - 16)]\n" + " .cfi_offset x21, -24\n" + " .cfi_offset x20, -32\n" + " stp x22, x23, [sp, #(96 - 32)]\n" + " .cfi_offset x23, -40\n" + " .cfi_offset x22, -48\n" + + " ; Save the input parameters.\n" + " mov x20, x0\n" + " mov x21, x1\n" + "\n" + " ;\n" + " ; Is the module already loaded?\n" + " ;\n" + " adrp x0, g_hMod@PAGE\n" + " ldr x0, [x0, g_hMod@PAGEOFF]\n" + " cmp x0, #0\n" + " b.ne Lexplicit_loaded_module\n" + "\n" + , + cchLibBaseName, g_pszLibrary, + pszNmPfx, cchLibBaseName, g_pszLibrary, + pszNmPfx, cchLibBaseName, g_pszLibrary); + fprintf(pOutput, + "Lexplicit_load_module:\n" + " ; Call SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo);\n" + " mov x3, #0\n" + " mov x2, #0\n" + " adrp x1, g_hMod@PAGE\n" + " add x1, x1, g_hMod@PAGEOFF\n" + " adrp x0, g_szLibrary@PAGE\n" + " add x0, x0, g_szLibrary@PAGEOFF\n" + " bl %sSUPR3HardenedLdrLoadAppPriv\n" + " cmp x0, #0\n" + " b.ne Lexplicit_load_return\n" + "\n" + , pszNmPfx); + + fprintf(pOutput, + " ;\n" + " ; Resolve the imports too if requested to do so.\n" + " ;\n" + "Lexplicit_loaded_module:\n" + " cmp w20, #0\n" + " b.eq Lexplicit_load_return\n" + "\n" + " adrp x22, g_szzNames@PAGE\n" + " add x22, x22, g_szzNames@PAGEOFF\n" + " adrp x23, g_apfnImports@PAGE\n" + " add x23, x23, g_apfnImports@PAGEOFF\n" + "Lexplicit_load_next_import:\n" + " ldr x0, [x23]\n" + " cmp x0, #0\n" + " b.eq Lexplicit_load_return\n" + "\n" + " ; Get the module handle and call RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue)\n" + " adrp x0, g_hMod@PAGE\n" + " ldr x0, [x0, g_hMod@PAGEOFF]\n" + " mov x1, x22\n" + " mov x2, x23\n" + " bl %sRTLdrGetSymbol\n" + " cmp x0, #0\n" + " b.ne Lexplicit_load_symbol_error\n" + "\n" + " ; Advance.\n" + " add x23, x23, #8\n" + "Lexplict_load_advance_string:\n" + " ldrb w0, [x22]\n" + " add x22, x22, #1\n" + " cmp w0, #0\n" + " b.ne Lexplict_load_advance_string\n" + " b Lexplicit_load_next_import\n" + "\n" + " ;\n" + " ; Error loading a symbol. Call RTErrInfoSet(PRTERRINFO pErrInfo, int rc, const char *pszMsg) on pErrInfo (preserves x0).\n" + " ;\n" + "Lexplicit_load_symbol_error:\n" + " mov x2, x22\n" + " mov x1, x0\n" + " mov x0, x21\n" + " bl %sRTErrInfoSet\n" + " b Lexplicit_load_return" + " " + "\n" + "Lexplicit_load_return:\n" + " ldp x22, x23, [sp, #(96 - 32)]\n" + " .cfi_restore x23\n" + " .cfi_restore x22\n" + " ldp x20, x21, [sp, #(96 - 16)]\n" + " .cfi_restore x21\n" + " .cfi_restore x20\n" + "\n" + " ldp x29, x30, [sp, #96]\n" + " .cfi_restore x29\n" + " .cfi_restore x30\n" + " add sp, sp, #(16 + 96)\n" + " ret\n" + " .cfi_endproc\n" + "\n" + "\n" + , pszNmPfx, pszNmPfx); + } + + 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) + { + switch (g_enmTarget) + { + case RTLDRARCH_AMD64: + case RTLDRARCH_X86_32: + rcExit = generateOutputInnerX86AndAMD64(pOutput); + break; + case RTLDRARCH_ARM64: + rcExit = generateOutputInnerArm64(pOutput); + break; + default: + rcExit = RTEXITCODE_FAILURE; + break; + } + 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 [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-2016 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]; + } + else if (!strcmp(psz, "--explicit-load-function")) + g_fWithExplictLoadFunction = true; + else if (!strcmp(psz, "--no-explicit-load-function")) + g_fWithExplictLoadFunction = false; + else if (!strcmp(psz, "--system")) + g_fSystemLibrary = true; + /** @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: 153224 $\n"); + return RTEXITCODE_SUCCESS; + } + else + { + fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz); + return RTEXITCODE_SYNTAX; + } + } + else + { + if (g_cInputs >= RT_ELEMENTS(g_apszInputs)) + { + fprintf(stderr, "syntax error: Too many input files, max is %d.\n", (int)RT_ELEMENTS(g_apszInputs)); + return RTEXITCODE_SYNTAX; + } + g_apszInputs[g_cInputs++] = argv[i]; + } + } + if (g_cInputs == 0) + { + 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 = parseInputs(); + if (rcExit == RTEXITCODE_SUCCESS) + rcExit = generateOutput(); + return rcExit; +} + |