1  /* $Id: VBoxDef2LazyLoad.cpp 105813 20240822 11:38:31Z vboxsync $ */


2  /** @file


3  * VBoxDef2LazyLoad  Lazy Library Loader Generator.


4  *


5  * @note Only tested on win.amd64 & darwin.amd64.


6  */


7 


8  /*


9  * Copyright (C) 20132023 Oracle and/or its affiliates.


10  *


11  * This file is part of VirtualBox base platform packages, as


12  * available from https://www.virtualbox.org.


13  *


14  * This program is free software; you can redistribute it and/or


15  * modify it under the terms of the GNU General Public License


16  * as published by the Free Software Foundation, in version 3 of the


17  * License.


18  *


19  * This program is distributed in the hope that it will be useful, but


20  * WITHOUT ANY WARRANTY; without even the implied warranty of


21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU


22  * General Public License for more details.


23  *


24  * You should have received a copy of the GNU General Public License


25  * along with this program; if not, see <https://www.gnu.org/licenses>.


26  *


27  * SPDXLicenseIdentifier: GPL3.0only


28  */


29 


30 


31  /*********************************************************************************************************************************


32  * Header Files *


33  *********************************************************************************************************************************/


34  #include <ctype.h>


35  #include <stdio.h>


36  #include <string.h>


37  #include <stdlib.h>


38  #include <iprt/types.h>


39  #include <iprt/ldr.h> /* For RTLDRARCH. */


40 


41 


42  /*********************************************************************************************************************************


43  * Structures and Typedefs *


44  *********************************************************************************************************************************/


45  typedef struct MYEXPORT


46  {


47  struct MYEXPORT *pNext;


48  /** Pointer to unmangled name for stdcall (after szName), NULL if not. */


49  char *pszUnstdcallName;


50  /** Pointer to the exported name. */


51  char const *pszExportedNm;


52  unsigned uOrdinal;


53  /** NONAME. */


54  bool fNoName;


55  /** DATA symbol if true, otherwise function. */


56  bool fData;


57  char szName[1];


58  } MYEXPORT;


59  typedef MYEXPORT *PMYEXPORT;


60 


61 


62  /*********************************************************************************************************************************


63  * Global Variables *


64  *********************************************************************************************************************************/


65  /** @name Options


66  * @{ */


67  static const char *g_pszOutput = NULL;


68  static const char *g_pszLibrary = NULL;


69  static const char *g_apszInputs[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };


70  static unsigned g_cInputs = 0;


71  static bool g_fIgnoreData = true;


72  static bool g_fWithExplictLoadFunction = false;


73  static bool g_fSystemLibrary = false;


74  #if defined(RT_ARCH_AMD64)


75  static RTLDRARCH g_enmTarget = RTLDRARCH_AMD64;


76  #elif defined(RT_ARCH_X86)


77  static RTLDRARCH g_enmTarget = RTLDRARCH_X86_32;


78  #elif defined(RT_ARCH_ARM64)


79  static RTLDRARCH g_enmTarget = RTLDRARCH_ARM64;


80  #else


81  # error "Port me!"


82  #endif


83  /** @} */


84 


85  /** Pointer to the export name list head. */


86  static PMYEXPORT g_pExpHead = NULL;


87  /** Pointer to the next pointer for insertion. */


88  static PMYEXPORT *g_ppExpNext = &g_pExpHead;


89 


90 


91 


92  #if 0 /* unused */


93  static const char *leftStrip(const char *psz)


94  {


95  while (isspace(*psz))


96  psz++;


97  return psz;


98  }


99  #endif


100 


101 


102  static char *leftStrip(char *psz)


103  {


104  while (isspace(*psz))


105  psz++;


106  return psz;


107  }


108 


109 


110  static unsigned wordLength(const char *pszWord)


111  {


112  unsigned off = 0;


113  char ch;


114  while ( (ch = pszWord[off]) != '\0'


115  && ch != '='


116  && ch != ','


117  && ch != ':'


118  && !isspace(ch) )


119  off++;


120  return off;


121  }


122 


123 


124  /**


125  * Parses the module definition file, collecting export information.


126  *


127  * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full


128  * details has been displayed.


129  * @param pInput The input stream.


130  */


131  static RTEXITCODE parseInputInner(FILE *pInput, const char *pszInput)


132  {


133  /*


134  * Process the file linebyline.


135  */


136  bool fInExports = false;


137  unsigned iLine = 0;


138  char szLine[16384];


139  while (fgets(szLine, sizeof(szLine), pInput))


140  {


141  iLine++;


142 


143  /*


144  * Strip leading and trailing spaces from the line as well as


145  * trailing comments.


146  */


147  char *psz = leftStrip(szLine);


148  if (*psz == ';')


149  continue; /* comment line. */


150 


151  char *pszComment = strchr(psz, ';');


152  if (pszComment)


153  *pszComment = '\0';


154 


155  unsigned cch = (unsigned)strlen(psz);


156  while (cch > 0 && (isspace(psz[cch  1])  psz[cch  1] == '\r'  psz[cch  1] == '\n'))


157  psz[cch] = '\0';


158 


159  if (!cch)


160  continue;


161 


162  /*


163  * Check for known directives.


164  */


165  size_t cchWord0 = wordLength(psz);


166  #define WORD_CMP(pszWord1, cchWord1, szWord2) \


167  ( (cchWord1) == sizeof(szWord2)  1 && memcmp(pszWord1, szWord2, sizeof(szWord2)  1) == 0 )


168  if (WORD_CMP(psz, cchWord0, "EXPORTS"))


169  {


170  fInExports = true;


171 


172  /* In case there is an export on the same line. (Really allowed?) */


173  psz = leftStrip(psz + sizeof("EXPORTS")  1);


174  if (!*psz)


175  continue;


176  }


177  /* Directives that we don't care about, but need to catch in order to


178  terminate the EXPORTS section in a timely manner. */


179  else if ( WORD_CMP(psz, cchWord0, "NAME")


180   WORD_CMP(psz, cchWord0, "LIBRARY")


181   WORD_CMP(psz, cchWord0, "DESCRIPTION")


182   WORD_CMP(psz, cchWord0, "STACKSIZE")


183   WORD_CMP(psz, cchWord0, "SECTIONS")


184   WORD_CMP(psz, cchWord0, "SEGMENTS")


185   WORD_CMP(psz, cchWord0, "VERSION")


186  )


187  {


188  fInExports = false;


189  }


190 


191  /*


192  * Process exports:


193  * entryname[=internalname] [@ordinal[ ][NONAME]] [DATA] [PRIVATE]


194  */


195  if (fInExports)


196  {


197  const char *pchName = psz;


198  unsigned cchName = wordLength(psz);


199 


200  psz = leftStrip(psz + cchName);


201  if (*psz == '=')


202  {


203  psz = leftStrip(psz + 1);


204  psz = leftStrip(psz + wordLength(psz));


205  }


206 


207  bool fNoName = false;


208  unsigned uOrdinal = ~0U;


209  if (*psz == '@')


210  {


211  psz++;


212  if (!isdigit(*psz))


213  {


214  fprintf(stderr, "%s:%u: error: Invalid ordinal spec.\n", pszInput, iLine);


215  return RTEXITCODE_FAILURE;


216  }


217  uOrdinal = *psz++  '0';


218  while (isdigit(*psz))


219  {


220  uOrdinal *= 10;


221  uOrdinal += *psz++  '0';


222  }


223  psz = leftStrip(psz);


224  cch = wordLength(psz);


225  if (WORD_CMP(psz, cch, "NONAME"))


226  {


227  fNoName = true;


228  psz = leftStrip(psz + cch);


229  }


230  }


231 


232  bool fData = false;


233  while (*psz)


234  {


235  cch = wordLength(psz);


236  if (WORD_CMP(psz, cch, "DATA"))


237  {


238  fData = true;


239  if (!g_fIgnoreData)


240  {


241  fprintf(stderr, "%s:%u: error: Cannot process DATA export '%.*s'.\n",


242  pszInput, iLine, cchName, pchName);


243  return RTEXITCODE_SUCCESS;


244  }


245  }


246  else if (WORD_CMP(psz, cch, "PRIVATE"))


247  {


248  fprintf(stderr, "%s:%u: error: Cannot process PRIVATE export '%.*s'.\n",


249  pszInput, iLine, cchName, pchName);


250  return RTEXITCODE_SUCCESS;


251  }


252  else


253  {


254  fprintf(stderr, "%s:%u: error: Unknown keyword: %.*s.\n", pszInput, iLine, cch, psz);


255  return RTEXITCODE_FAILURE;


256  }


257  psz = leftStrip(psz + cch);


258  }


259 


260  /*


261  * Check for stdcall mangling.


262  */


263  size_t cbExp = sizeof(MYEXPORT) + cchName;


264  unsigned cchStdcall = 0;


265  if (cchName > 3 && *pchName == '_' && isdigit(pchName[cchName  1]))


266  {


267  if (cchName > 3 && pchName[cchName  2] == '@')


268  cchStdcall = 2;


269  else if (cchName > 4 && pchName[cchName  3] == '@' && isdigit(pchName[cchName  2]))


270  cchStdcall = 3;


271  if (cchStdcall)


272  cbExp += cchName  1  cchStdcall;


273  }


274 


275  /*


276  * Add the export.


277  */


278  PMYEXPORT pExp = (PMYEXPORT)malloc(cbExp);


279  if (!pExp)


280  {


281  fprintf(stderr, "%s:%u: error: Out of memory.\n", pszInput, iLine);


282  return RTEXITCODE_FAILURE;


283  }


284  memcpy(pExp>szName, pchName, cchName);


285  pExp>szName[cchName] = '\0';


286  if (!cchStdcall)


287  {


288  pExp>pszUnstdcallName = NULL;


289  pExp>pszExportedNm = pExp>szName;


290  }


291  else


292  {


293  pExp>pszUnstdcallName = &pExp>szName[cchName + 1];


294  memcpy(pExp>pszUnstdcallName, pchName + 1, cchName  1  cchStdcall);


295  pExp>pszUnstdcallName[cchName  1  cchStdcall] = '\0';


296  pExp>pszExportedNm = pExp>pszUnstdcallName;


297  }


298  pExp>uOrdinal = uOrdinal;


299  pExp>fNoName = fNoName;


300  pExp>fData = fData;


301  pExp>pNext = NULL;


302  *g_ppExpNext = pExp;


303  g_ppExpNext = &pExp>pNext;


304  }


305  }


306 


307  /*


308  * Why did we quit the loop, EOF or error?


309  */


310  if (feof(pInput))


311  return RTEXITCODE_SUCCESS;


312  fprintf(stderr, "error: Incompletely read '%s' (iLine=%u).\n", pszInput, iLine);


313  return RTEXITCODE_FAILURE;


314  }


315 


316 


317  /**


318  * Parses a_apszInputs, populating the list pointed to by g_pExpHead.


319  *


320  * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full


321  * details has been displayed.


322  */


323  static RTEXITCODE parseInputs(void)


324  {


325  RTEXITCODE rcExit = RTEXITCODE_SUCCESS;


326  for (unsigned i = 0; i < g_cInputs; i++)


327  {


328  FILE *pInput = fopen(g_apszInputs[i], "r");


329  if (pInput)


330  {


331  RTEXITCODE rcExit2 = parseInputInner(pInput, g_apszInputs[i]);


332  fclose(pInput);


333  if (rcExit2 == RTEXITCODE_SUCCESS && !g_pExpHead)


334  {


335  fprintf(stderr, "error: Found no exports in '%s'.\n", g_apszInputs[i]);


336  rcExit2 = RTEXITCODE_FAILURE;


337  }


338  if (rcExit2 != RTEXITCODE_SUCCESS)


339  rcExit = rcExit2;


340  }


341  else


342  {


343  fprintf(stderr, "error: Failed to open '%s' for reading.\n", g_apszInputs[i]);


344  rcExit = RTEXITCODE_FAILURE;


345  }


346  }


347  return rcExit;


348  }


349 


350 


351  /**


352  * Generates the assembly source code for AMD64 and x86, writing it


353  * to @a pOutput.


354  *


355  * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full


356  * details has been displayed.


357  * @param pOutput The output stream (caller checks it for errors


358  * when closing).


359  */


360  static RTEXITCODE generateOutputInnerX86AndAMD64(FILE *pOutput)


361  {


362  fprintf(pOutput, ";;\n");


363  for (unsigned i = 0; i < g_cInputs; i++)


364  fprintf(pOutput, ";; Autogenerated from '%s'.\n", g_apszInputs[i]);


365 


366  fprintf(pOutput,


367  ";; DO NOT EDIT!\n"


368  ";;\n"


369  "\n"


370  "\n"


371  "%%include \"iprt/asmdefs.mac\"\n"


372  "\n"


373  "\n");


374 


375  /*


376  * Put the thunks first for alignment and other reasons. It's the hot part of the code.


377  */


378  fprintf(pOutput,


379  ";\n"


380  "; Thunks.\n"


381  ";\n"


382  "BEGINCODE\n");


383  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


384  if (pExp>fData)


385  fprintf(pOutput,


386  "BEGINPROC LazyGetPtr_%s\n"


387  " mov xAX, [NAME(g_LazyPtr_%s) xWrtRIP]\n"


388  " test xAX, xAX\n"


389  " jz ___LazyLoad___%s\n"


390  " ret\n"


391  "ENDPROC LazyGetPtr_%s\n",


392  pExp>szName, pExp>szName, pExp>szName, pExp>szName);


393  else if (!pExp>pszUnstdcallName)


394  fprintf(pOutput,


395  "BEGINPROC %s\n"


396  " jmp RTCCPTR_PRE [NAME(g_pfn%s) xWrtRIP]\n"


397  "ENDPROC %s\n",


398  pExp>szName, pExp>szName, pExp>szName);


399  else


400  fprintf(pOutput,


401  "%%ifdef RT_ARCH_X86\n"


402  "global %s\n"


403  "%s:\n"


404  " jmp RTCCPTR_PRE [NAME(g_pfn%s) xWrtRIP]\n"


405  "%%else\n"


406  "BEGINPROC %s\n"


407  " jmp RTCCPTR_PRE [NAME(g_pfn%s) xWrtRIP]\n"


408  "ENDPROC %s\n"


409  "%%endif\n",


410  pExp>szName, pExp>szName, pExp>pszUnstdcallName,


411  pExp>pszUnstdcallName, pExp>pszUnstdcallName, pExp>pszUnstdcallName);


412 


413  fprintf(pOutput,


414  "\n"


415  "\n");


416 


417  /*


418  * Import pointers


419  */


420  fprintf(pOutput,


421  ";\n"


422  "; Import pointers. Initialized to point to lazy loading stubs.\n"


423  ";\n"


424  "BEGINDATA\n"


425  "g_apfnImports:\n");


426  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


427  if (pExp>fData)


428  fprintf(pOutput,


429  "%%ifdef ASM_FORMAT_PE\n"


430  ";@todo\n"


431  "%%endif\n"


432  "global NAME(g_LazyPtr_%s)\n"


433  "NAME(g_LazyPtr_%s): RTCCPTR_DEF 0\n",


434  pExp>pszExportedNm, pExp>pszExportedNm);


435  else if (pExp>pszUnstdcallName)


436  fprintf(pOutput,


437  "%%ifdef ASM_FORMAT_PE\n"


438  " %%ifdef RT_ARCH_X86\n"


439  "global __imp_%s\n"


440  "__imp_%s:\n"


441  " %%else\n"


442  "global __imp_%s\n"


443  "__imp_%s:\n"


444  " %%endif\n"


445  "%%endif\n"


446  "NAME(g_pfn%s) RTCCPTR_DEF ___LazyLoad___%s\n"


447  "\n",


448  pExp>szName,


449  pExp>szName,


450  pExp>pszUnstdcallName,


451  pExp>pszUnstdcallName,


452  pExp>pszExportedNm,


453  pExp>pszExportedNm);


454  else


455  fprintf(pOutput,


456  "%%ifdef ASM_FORMAT_PE\n"


457  "global __imp_%s\n"


458  "__imp_%s:\n"


459  "%%endif\n"


460  "NAME(g_pfn%s) RTCCPTR_DEF ___LazyLoad___%s\n"


461  "\n",


462  pExp>szName,


463  pExp>szName,


464  pExp>pszExportedNm,


465  pExp>pszExportedNm);


466  fprintf(pOutput,


467  "RTCCPTR_DEF 0 ; Terminator entry for traversal.\n"


468  "\n"


469  "\n");


470 


471  /*


472  * Now for the less important stuff, starting with the names.


473  *


474  * We keep the names separate so we can traverse them in parallel to


475  * g_apfnImports in the loadeverything routine further down.


476  */


477  fprintf(pOutput,


478  ";\n"


479  "; Imported names.\n"


480  ";\n"


481  "BEGINCODE\n"


482  "g_szLibrary: db '%s',0\n"


483  "\n"


484  "g_szzNames:\n",


485  g_pszLibrary);


486  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


487  if (!pExp>fNoName)


488  fprintf(pOutput, " g_sz%s:\n db '%s',0\n", pExp>pszExportedNm, pExp>pszExportedNm);


489  else


490  fprintf(pOutput, " g_sz%s:\n db '#%u',0\n", pExp>pszExportedNm, pExp>uOrdinal);


491  fprintf(pOutput,


492  "g_EndOfNames: db 0\n"


493  "\n"


494  "g_szFailLoadFmt: db 'Lazy loader failed to load \"%%s\": %%Rrc', 10, 0\n"


495  "g_szFailResolveFmt: db 'Lazy loader failed to resolve symbol \"%%s\" in \"%%s\": %%Rrc', 10, 0\n"


496  "\n"


497  "\n");


498 


499  /*


500  * The per import lazy load code.


501  */


502  fprintf(pOutput,


503  ";\n"


504  "; Lazy load+resolve stubs.\n"


505  ";\n"


506  "BEGINCODE\n");


507  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


508  {


509  if (!pExp>fNoName)


510  fprintf(pOutput,


511  "___LazyLoad___%s:\n"


512  /* "int3\n" */


513  "%%ifdef RT_ARCH_AMD64\n"


514  " lea rax, [g_sz%s wrt rip]\n"


515  " lea r10, [NAME(%s%s) wrt rip]\n"


516  " call LazyLoadResolver\n"


517  "%%elifdef RT_ARCH_X86\n"


518  " push g_sz%s\n"


519  " push NAME(%s%s)\n"


520  " call LazyLoadResolver\n"


521  " add esp, 8h\n"


522  "%%else\n"


523  " %%error \"Unsupported architecture\"\n"


524  "%%endif\n"


525  ,


526  pExp>pszExportedNm,


527  pExp>pszExportedNm,


528  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm,


529  pExp>pszExportedNm,


530  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm);


531  else


532  fprintf(pOutput,


533  "___LazyLoad___%s:\n"


534  /* "int3\n" */


535  "%%ifdef RT_ARCH_AMD64\n"


536  " mov eax, %u\n"


537  " lea r10, [NAME(%s%s) wrt rip]\n"


538  " call LazyLoadResolver\n"


539  "%%elifdef RT_ARCH_X86\n"


540  " push %u\n"


541  " push NAME(%s%s)\n"


542  " call LazyLoadResolver\n"


543  " add esp, 8h\n"


544  "%%else\n"


545  " %%error \"Unsupported architecture\"\n"


546  "%%endif\n"


547  ,


548  pExp>pszExportedNm,


549  pExp>uOrdinal,


550  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm,


551  pExp>uOrdinal,


552  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm);


553  if (pExp>fData)


554  fprintf(pOutput, " jmp NAME(LazyGetPtr_%s)\n", pExp>szName);


555  else if (!pExp>pszUnstdcallName)


556  fprintf(pOutput, " jmp NAME(%s)\n", pExp>szName);


557  else


558  fprintf(pOutput,


559  "%%ifdef RT_ARCH_X86\n"


560  " jmp %s\n"


561  "%%else\n"


562  " jmp NAME(%s)\n"


563  "%%endif\n"


564  ,


565  pExp>szName, pExp>pszUnstdcallName);


566  fprintf(pOutput, "\n");


567  }


568  fprintf(pOutput,


569  "\n"


570  "\n"


571  "\n");


572 


573  /*


574  * The code that does the loading and resolving.


575  */


576  fprintf(pOutput,


577  ";\n"


578  "; The module handle.\n"


579  ";\n"


580  "BEGINDATA\n"


581  "g_hMod RTCCPTR_DEF 0\n"


582  "\n"


583  "\n"


584  "\n");


585 


586  /*


587  * How we load the module needs to be selectable later on.


588  *


589  * The LazyLoading routine returns the module handle in RCX/ECX, caller


590  * saved all necessary registers.


591  */


592  if (!g_fSystemLibrary)


593  fprintf(pOutput,


594  ";\n"


595  ";SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod,\n"


596  "; uint32_t fFlags, PRTERRINFO pErrInfo);\n"


597  ";\n"


598  "EXTERN_IMP2 SUPR3HardenedLdrLoadAppPriv\n"


599  "%%ifdef IN_RT_R3\n"


600  "extern NAME(RTAssertMsg2Weak)\n"


601  "%%else\n"


602  "EXTERN_IMP2 RTAssertMsg2Weak\n"


603  "%%endif\n"


604  "BEGINCODE\n"


605  "\n"


606  "LazyLoading:\n"


607  " mov xCX, [g_hMod xWrtRIP]\n"


608  " or xCX, xCX\n"


609  " jnz .return\n"


610  "\n"


611  "%%ifdef ASM_CALL64_GCC\n"


612  " xor rcx, rcx ; pErrInfo\n"


613  " xor rdx, rdx ; fFlags (local load)\n"


614  " lea rsi, [g_hMod wrt rip] ; phLdrMod\n"


615  " lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"


616  " sub rsp, 08h\n"


617  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


618  " add rsp, 08h\n"


619  "\n"


620  "%%elifdef ASM_CALL64_MSC\n"


621  " xor r9, r9 ; pErrInfo\n"


622  " xor r8, r8 ; fFlags (local load)\n"


623  " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"


624  " lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"


625  " sub rsp, 28h\n"


626  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


627  " add rsp, 28h\n"


628  "\n"


629  "%%elifdef RT_ARCH_X86\n"


630  " sub xSP, 0ch\n"


631  " push 0 ; pErrInfo\n"


632  " push 0 ; fFlags (local load)\n"


633  " push g_hMod ; phLdrMod\n"


634  " push g_szLibrary ; pszFilename\n"


635  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


636  " add esp, 1ch\n"


637  "%%else\n"


638  " %%error \"Unsupported architecture\"\n"


639  "%%endif\n");


640  else


641  fprintf(pOutput,


642  ";\n"


643  "; RTDECL(int) RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod);\n"


644  ";\n"


645  "%%ifdef IN_RT_R3\n"


646  "extern NAME(RTLdrLoadSystem)\n"


647  "extern NAME(RTAssertMsg2Weak)\n"


648  "%%else\n"


649  "EXTERN_IMP2 RTLdrLoadSystem\n"


650  "EXTERN_IMP2 RTAssertMsg2Weak\n"


651  "%%endif\n"


652  "BEGINCODE\n"


653  "\n"


654  "LazyLoading:\n"


655  " mov xCX, [g_hMod xWrtRIP]\n"


656  " or xCX, xCX\n"


657  " jnz .return\n"


658  "\n"


659  "%%ifdef ASM_CALL64_GCC\n"


660  " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"


661  " mov esi, 1 ; fNoUnload=true\n"


662  " lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"


663  " sub rsp, 08h\n"


664  " %%ifdef IN_RT_R3\n"


665  " call NAME(RTLdrLoadSystem)\n"


666  " %%else\n"


667  " call IMP2(RTLdrLoadSystem)\n"


668  " %%endif\n"


669  " add rsp, 08h\n"


670  "\n"


671  "%%elifdef ASM_CALL64_MSC\n"


672  " lea r8, [g_hMod wrt rip] ; phLdrMod\n"


673  " mov edx, 1 ; fNoUnload=true\n"


674  " lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"


675  " sub rsp, 28h\n"


676  " %%ifdef IN_RT_R3\n"


677  " call NAME(RTLdrLoadSystem)\n"


678  " %%else\n"


679  " call IMP2(RTLdrLoadSystem)\n"


680  " %%endif\n"


681  " add rsp, 28h\n"


682  "\n"


683  "%%elifdef RT_ARCH_X86\n"


684  " push g_hMod ; phLdrMod\n"


685  " push 1 ; fNoUnload=true\n"


686  " push g_szLibrary ; pszFilename\n"


687  " %%ifdef IN_RT_R3\n"


688  " call NAME(RTLdrLoadSystem)\n"


689  " %%else\n"


690  " call IMP2(RTLdrLoadSystem)\n"


691  " %%endif\n"


692  " add esp, 0ch\n"


693  "%%else\n"


694  " %%error \"Unsupported architecture\"\n"


695  "%%endif\n");


696  fprintf(pOutput,


697  " or eax, eax\n"


698  " jnz .badload\n"


699  " mov xCX, [g_hMod xWrtRIP]\n"


700  ".return:\n"


701  " ret\n"


702  "\n"


703  ".badload:\n"


704  "%%ifdef ASM_CALL64_GCC\n"


705  " mov edx, eax\n"


706  " lea rsi, [g_szLibrary wrt rip]\n"


707  " lea rdi, [g_szFailLoadFmt wrt rip]\n"


708  " sub rsp, 08h\n"


709  "%%elifdef ASM_CALL64_MSC\n"


710  " mov r8d, eax\n"


711  " lea rdx, [g_szLibrary wrt rip]\n"


712  " lea rcx, [g_szFailLoadFmt wrt rip]\n"


713  " sub rsp, 28h\n"


714  "%%elifdef RT_ARCH_X86\n"


715  " push eax\n"


716  " push g_szLibrary\n"


717  " push g_szFailLoadFmt\n"


718  "%%endif\n"


719  "%%ifdef IN_RT_R3\n"


720  " call NAME(RTAssertMsg2Weak)\n"


721  "%%else\n"


722  " call IMP2(RTAssertMsg2Weak)\n"


723  "%%endif\n"


724  ".badloadloop:\n"


725  " int3\n"


726  " jmp .badloadloop\n"


727  "LazyLoading_End:\n"


728  "\n"


729  "\n");


730 


731 


732  fprintf(pOutput,


733  ";\n"


734  ";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n"


735  ";\n"


736  "%%ifdef IN_RT_R3\n"


737  "extern NAME(RTLdrGetSymbol)\n"


738  "%%else\n"


739  "EXTERN_IMP2 RTLdrGetSymbol\n"


740  "%%endif\n"


741  "BEGINCODE\n"


742  "LazyLoadResolver:\n"


743  "%%ifdef RT_ARCH_AMD64\n"


744  " push rbp\n"


745  " mov rbp, rsp\n"


746  " push r15\n"


747  " push r14\n"


748  " mov r15, rax ; name\n"


749  " mov r14, r10 ; ppfn\n"


750  " push r9\n"


751  " push r8\n"


752  " push rcx\n"


753  " push rdx\n"


754  " push r12\n"


755  " %%ifdef ASM_CALL64_GCC\n"


756  " push rsi\n"


757  " push rdi\n"


758  " mov r12, rsp\n"


759  " %%else\n"


760  " mov r12, rsp\n"


761  " sub rsp, 20h\n"


762  " %%endif\n"


763  " and rsp, 0fffffff0h ; Try make sure the stack is aligned\n"


764  "\n"


765  " call LazyLoading ; returns handle in rcx\n"


766  " %%ifdef ASM_CALL64_GCC\n"


767  " mov rdi, rcx ; hLdrMod\n"


768  " mov rsi, r15 ; pszSymbol\n"


769  " mov rdx, r14 ; ppvValue\n"


770  " %%else\n"


771  " mov rdx, r15 ; pszSymbol\n"


772  " mov r8, r14 ; ppvValue\n"


773  " %%endif\n"


774  " %%ifdef IN_RT_R3\n"


775  " call NAME(RTLdrGetSymbol)\n"


776  " %%else\n"


777  " call IMP2(RTLdrGetSymbol)\n"


778  " %%endif\n"


779  " or eax, eax\n"


780  " jnz .badsym\n"


781  "\n"


782  " mov rsp, r12\n"


783  " %%ifdef ASM_CALL64_GCC\n"


784  " pop rdi\n"


785  " pop rsi\n"


786  " %%endif\n"


787  " pop r12\n"


788  " pop rdx\n"


789  " pop rcx\n"


790  " pop r8\n"


791  " pop r9\n"


792  " pop r14\n"


793  " pop r15\n"


794  " leave\n"


795  "\n"


796  "%%elifdef RT_ARCH_X86\n"


797  " push ebp\n"


798  " mov ebp, esp\n"


799  " push eax\n"


800  " push ecx\n"


801  " push edx\n"


802  " and esp, 0fffffff0h\n"


803  "\n"


804  ".loaded:\n"


805  " call LazyLoading ; returns handle in ecx\n"


806  " push dword [ebp + 8] ; value addr\n"


807  " push dword [ebp + 12] ; symbol name\n"


808  " push ecx\n"


809  " %%ifdef IN_RT_R3\n"


810  " call NAME(RTLdrGetSymbol)\n"


811  " %%else\n"


812  " call IMP2(RTLdrGetSymbol)\n"


813  " %%endif\n"


814  " or eax, eax\n"


815  " jnz .badsym\n"


816  " lea esp, [ebp  0ch]\n"


817  " pop edx\n"


818  " pop ecx\n"


819  " pop eax\n"


820  " leave\n"


821  "%%else\n"


822  " %%error \"Unsupported architecture\"\n"


823  "%%endif\n"


824  " ret\n"


825  "\n"


826  ".badsym:\n"


827  "%%ifdef ASM_CALL64_GCC\n"


828  " mov ecx, eax\n"


829  " lea rdx, [g_szLibrary wrt rip]\n"


830  " mov rsi, r15\n"


831  " lea rdi, [g_szFailResolveFmt wrt rip]\n"


832  " sub rsp, 08h\n"


833  "%%elifdef ASM_CALL64_MSC\n"


834  " mov r9d, eax\n"


835  " mov r8, r15\n"


836  " lea rdx, [g_szLibrary wrt rip]\n"


837  " lea rcx, [g_szFailResolveFmt wrt rip]\n"


838  " sub rsp, 28h\n"


839  "%%elifdef RT_ARCH_X86\n"


840  " push eax\n"


841  " push dword [ebp + 12]\n"


842  " push g_szLibrary\n"


843  " push g_szFailResolveFmt\n"


844  "%%endif\n"


845  "%%ifdef IN_RT_R3\n"


846  " call NAME(RTAssertMsg2Weak)\n"


847  "%%else\n"


848  " call IMP2(RTAssertMsg2Weak)\n"


849  "%%endif\n"


850  ".badsymloop:\n"


851  " int3\n"


852  " jmp .badsymloop\n"


853  "\n"


854  "LazyLoadResolver_End:\n"


855  "\n"


856  "\n"


857  );


858 


859 


860 


861  /*


862  * C callable method for explicitly loading the library and optionally


863  * resolving all the imports.


864  */


865  if (g_fWithExplictLoadFunction)


866  {


867  int cchLibBaseName = (int)(strchr(g_pszLibrary, '.') ? strchr(g_pszLibrary, '.')  g_pszLibrary : strlen(g_pszLibrary));


868  fprintf(pOutput,


869  ";;\n"


870  "; ExplicitlyLoad%.*s(bool fResolveAllImports, pErrInfo);\n"


871  ";\n"


872  "%%ifdef IN_RT_R3\n"


873  "extern NAME(RTErrInfoSet)\n"


874  "%%else\n"


875  "EXTERN_IMP2 RTErrInfoSet\n"


876  "%%endif\n"


877  "BEGINCODE\n"


878  "BEGINPROC ExplicitlyLoad%.*s\n"


879  " push xBP\n"


880  " mov xBP, xSP\n"


881  " push xBX\n"


882  "%%ifdef ASM_CALL64_GCC\n"


883  " %%define pszCurStr r14\n"


884  " push r14\n"


885  "%%else\n"


886  " %%define pszCurStr xDI\n"


887  " push xDI\n"


888  "%%endif\n"


889  " sub xSP, 40h\n"


890  "\n"


891  " ;\n"


892  " ; Save parameters on stack (64bit only).\n"


893  " ;\n"


894  "%%ifdef ASM_CALL64_GCC\n"


895  " mov [xBP  xCB * 3], rdi ; fResolveAllImports\n"


896  " mov [xBP  xCB * 4], rsi ; pErrInfo\n"


897  "%%elifdef ASM_CALL64_MSC\n"


898  " mov [xBP  xCB * 3], rcx ; fResolveAllImports\n"


899  " mov [xBP  xCB * 4], rdx ; pErrInfo\n"


900  "%%endif\n"


901  "\n"


902  " ;\n"


903  " ; Is the module already loaded?\n"


904  " ;\n"


905  " cmp RTCCPTR_PRE [g_hMod xWrtRIP], 0\n"


906  " jnz .loaded\n"


907  "\n"


908  " ;\n"


909  " ; Load the module.\n"


910  " ;\n"


911  ,


912  cchLibBaseName, g_pszLibrary,


913  cchLibBaseName, g_pszLibrary);


914  if (!g_fSystemLibrary)


915  fprintf(pOutput,


916  "%%ifdef ASM_CALL64_GCC\n"


917  " mov rcx, [xBP  xCB * 4] ; pErrInfo\n"


918  " xor rdx, rdx ; fFlags (local load)\n"


919  " lea rsi, [g_hMod wrt rip] ; phLdrMod\n"


920  " lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"


921  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


922  "\n"


923  "%%elifdef ASM_CALL64_MSC\n"


924  " mov r9, [xBP  xCB * 4] ; pErrInfo\n"


925  " xor r8, r8 ; fFlags (local load)\n"


926  " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"


927  " lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"


928  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


929  "\n"


930  "%%elifdef RT_ARCH_X86\n"


931  " sub xSP, 0ch\n"


932  " push dword [xBP + 12] ; pErrInfo\n"


933  " push 0 ; fFlags (local load)\n"


934  " push g_hMod ; phLdrMod\n"


935  " push g_szLibrary ; pszFilename\n"


936  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


937  " add esp, 1ch\n"


938  "%%else\n"


939  " %%error \"Unsupported architecture\"\n"


940  "%%endif\n");


941  else


942  fprintf(pOutput,


943  "%%ifdef ASM_CALL64_GCC\n"


944  " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"


945  " mov esi, 1 ; fNoUnload=true\n"


946  " lea rdi, [g_szLibrary wrt rip] ; pszFilename\n"


947  " %%ifdef IN_RT_R3\n"


948  " call NAME(RTLdrLoadSystem)\n"


949  " %%else\n"


950  " call IMP2(RTLdrLoadSystem)\n"


951  " %%endif\n"


952  "\n"


953  "%%elifdef ASM_CALL64_MSC\n"


954  " lea r8, [g_hMod wrt rip] ; phLdrMod\n"


955  " mov edx, 1 ; fNoUnload=true\n"


956  " lea rcx, [g_szLibrary wrt rip] ; pszFilename\n"


957  " %%ifdef IN_RT_R3\n"


958  " call NAME(RTLdrLoadSystem)\n"


959  " %%else\n"


960  " call IMP2(RTLdrLoadSystem)\n"


961  " %%endif\n"


962  "\n"


963  "%%elifdef RT_ARCH_X86\n"


964  " push g_hMod ; phLdrMod\n"


965  " push 1 ; fNoUnload=true\n"


966  " push g_szLibrary ; pszFilename\n"


967  " %%ifdef IN_RT_R3\n"


968  " call NAME(RTLdrLoadSystem)\n"


969  " %%else\n"


970  " call IMP2(RTLdrLoadSystem)\n"


971  " %%endif\n"


972  " add esp, 0ch\n"


973  "%%else\n"


974  " %%error \"Unsupported architecture\"\n"


975  "%%endif\n");


976  fprintf(pOutput,


977  " or eax, eax\n"


978  " jnz .return\n"


979  "\n"


980  " ;\n"


981  " ; Resolve the imports too if requested to do so.\n"


982  " ;\n"


983  ".loaded:\n"


984  "%%ifdef ASM_ARCH_X86\n"


985  " cmp byte [xBP + 8], 0\n"


986  "%%else\n"


987  " cmp byte [xBP  xCB * 3], 0\n"


988  "%%endif\n"


989  " je .return\n"


990  "\n"


991  " lea pszCurStr, [g_szzNames xWrtRIP]\n"


992  " lea xBX, [g_apfnImports xWrtRIP]\n"


993  ".next_import:\n"


994  " cmp RTCCPTR_PRE [xBX], 0\n"


995  " je .return\n"


996  "%%ifdef ASM_CALL64_GCC\n"


997  " mov rdx, xBX ; ppvValue\n"


998  " mov rsi, pszCurStr ; pszSymbol\n"


999  " mov rdi, [g_hMod wrt rip] ; hLdrMod\n"


1000  " %%ifdef IN_RT_R3\n"


1001  " call NAME(RTLdrGetSymbol)\n"


1002  " %%else\n"


1003  " call IMP2(RTLdrGetSymbol)\n"


1004  " %%endif\n"


1005  "%%elifdef ASM_CALL64_MSC\n"


1006  " mov r8, xBX ; ppvValue\n"


1007  " mov rdx, pszCurStr ; pszSymbol\n"


1008  " mov rcx, [g_hMod wrt rip] ; pszSymbol\n"


1009  " %%ifdef IN_RT_R3\n"


1010  " call NAME(RTLdrGetSymbol)\n"


1011  " %%else\n"


1012  " call IMP2(RTLdrGetSymbol)\n"


1013  " %%endif\n"


1014  "%%else\n"


1015  " push xBX ; ppvValue\n"


1016  " push pszCurStr ; pszSymbol\n"


1017  " push RTCCPTR_PRE [g_hMod] ; hLdrMod\n"


1018  " %%ifdef IN_RT_R3\n"


1019  " call NAME(RTLdrGetSymbol)\n"


1020  " %%else\n"


1021  " call IMP2(RTLdrGetSymbol)\n"


1022  " %%endif\n"


1023  " add xSP, 0ch\n"


1024  "%%endif\n"


1025  " or eax, eax\n"


1026  " jnz .symbol_error\n"


1027  "\n"


1028  " ; Advance.\n"


1029  " add xBX, RTCCPTR_CB\n"


1030  " xor eax, eax\n"


1031  " mov xCX, 0ffffffffh\n"


1032  "%%ifdef ASM_CALL64_GCC\n"


1033  " mov xDI, pszCurStr\n"


1034  " repne scasb\n"


1035  " mov pszCurStr, xDI\n"


1036  "%%else\n"


1037  " repne scasb\n"


1038  "%%endif\n"


1039  " jmp .next_import\n"


1040  "\n"


1041  " ;\n"


1042  " ; Error loading a symbol. Call RTErrInfoSet on pErrInfo (preserves eax).\n"


1043  " ;\n"


1044  ".symbol_error:\n"


1045  "%%ifdef ASM_CALL64_GCC\n"


1046  " mov rdx, pszCurStr ; pszMsg\n"


1047  " mov esi, eax ; rc\n"


1048  " mov rdi, [xBP  xCB * 4] ; pErrInfo\n"


1049  " %%ifdef IN_RT_R3\n"


1050  " call NAME(RTErrInfoSet)\n"


1051  " %%else\n"


1052  " call IMP2(RTErrInfoSet)\n"


1053  " %%endif\n"


1054  "%%elifdef ASM_CALL64_MSC\n"


1055  " mov r8, pszCurStr ; pszMsg\n"


1056  " mov edx, eax ; rc\n"


1057  " mov rcx, [xBP  xCB * 4] ; pErrInfo\n"


1058  " %%ifdef IN_RT_R3\n"


1059  " call NAME(RTErrInfoSet)\n"


1060  " %%else\n"


1061  " call IMP2(RTErrInfoSet)\n"


1062  " %%endif\n"


1063  "%%else\n"


1064  " push pszCurStr ; pszMsg\n"


1065  " push eax ; pszSymbol\n"


1066  " push dword [xBP + 0ch] ; pErrInfo\n"


1067  " %%ifdef IN_RT_R3\n"


1068  " call NAME(RTErrInfoSet)\n"


1069  " %%else\n"


1070  " call IMP2(RTErrInfoSet)\n"


1071  " %%endif\n"


1072  " add xSP, 0ch\n"


1073  "%%endif\n"


1074  " "


1075  "\n"


1076  ".return:\n"


1077  " mov pszCurStr, [xBP  xCB * 2]\n"


1078  " mov xBX, [xBP  xCB * 1]\n"


1079  " leave\n"


1080  " ret\n"


1081  "ENDPROC ExplicitlyLoad%.*s\n"


1082  "\n"


1083  "\n"


1084  ,


1085  cchLibBaseName, g_pszLibrary);


1086  }


1087 


1088 


1089  return RTEXITCODE_SUCCESS;


1090  }


1091 


1092 


1093  /**


1094  * Generates the assembly source code for ARM64, writing it


1095  * to @a pOutput.


1096  *


1097  * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full


1098  * details has been displayed.


1099  * @param pOutput The output stream (caller checks it for errors


1100  * when closing).


1101  */


1102  static RTEXITCODE generateOutputInnerArm64(FILE *pOutput)


1103  {


1104  fprintf(pOutput, "/*\n");


1105  for (unsigned i = 0; i < g_cInputs; i++)


1106  fprintf(pOutput, " * Autogenerated from '%s'.\n", g_apszInputs[i]);


1107 


1108  fprintf(pOutput,


1109  " * DO NOT EDIT!\n"


1110  " */\n"


1111  "\n"


1112  "\n"


1113  "#include \"iprt/asmdefsarm.h\"\n"


1114  "\n"


1115  "\n");


1116 


1117  /*


1118  * Put the thunks first for alignment and other reasons. It's the hot part of the code.


1119  */


1120  fprintf(pOutput,


1121  "/*\n"


1122  " * Thunks.\n"


1123  " */\n"


1124  "BEGINCODE\n");


1125  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


1126  if (!pExp>fData)


1127  fprintf(pOutput,


1128  ".p2align 3\n"


1129  ".globl NAME(%s)\n"


1130  "NAME(%s):\n"


1131  " adrp x9, PAGE(NAME(g_pfn%s))\n"


1132  " ldr x9, [x9, PAGEOFF(NAME(g_pfn%s))]\n"


1133  " br x9\n",


1134  pExp>szName, pExp>szName, pExp>szName, pExp>szName);


1135  else


1136  fprintf(pOutput,


1137  ".p2align 3\n"


1138  ".globl NAME(LazyGetPtr_%s)\n"


1139  "NAME(LazyGetPtr_%s):\n"


1140  " adrp x9, PAGE(NAME(g_LazyPtr_%s))\n"


1141  " ldr x9, [x9, PAGEOFF(NAME(g_LazyPtr_%s))]\n"


1142  " cmp x9, #0\n"


1143  " b.eq ___LazyLoad___%s\n"


1144  " mov x0, x9\n"


1145  " ret\n",


1146  pExp>szName, pExp>szName, pExp>szName, pExp>szName, pExp>pszExportedNm);


1147  fprintf(pOutput,


1148  "ENDCODE\n"


1149  "\n"


1150  "\n");


1151 


1152  /*


1153  * Import pointers


1154  */


1155  fprintf(pOutput,


1156  "/*\n"


1157  " * Import pointers. Initialized to point to lazy loading stubs.\n"


1158  " */\n"


1159  "BEGINDATA\n"


1160  ".p2align 3\n"


1161  "g_apfnImports:\n");


1162  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


1163  if (!pExp>fData)


1164  fprintf(pOutput,


1165  ".globl __imp_%s\n"


1166  "__imp_%s:\n"


1167  ".globl NAME(g_pfn%s)\n"


1168  "NAME(g_pfn%s):\n"


1169  " .quad ___LazyLoad___%s\n"


1170  "\n",


1171  pExp>szName, pExp>szName,


1172  pExp>szName, pExp>szName,


1173  pExp>pszExportedNm);


1174  else


1175  fprintf(pOutput,


1176  ".globl NAME(g_LazyPtr_%s)\n"


1177  "NAME(g_LazyPtr_%s):\n"


1178  " .quad 0\n"


1179  "\n",


1180  pExp>szName, pExp>szName);


1181  fprintf(pOutput,


1182  " .quad 0 /* Terminator entry for traversal. */\n"


1183  "ENDDATA\n"


1184  "\n"


1185  "\n");


1186 


1187  /*


1188  * Now for the less important stuff, starting with the names.


1189  *


1190  * We keep the names separate so we can traverse them in parallel to


1191  * g_apfnImports in the loadeverything routine further down.


1192  */


1193  fprintf(pOutput,


1194  "/*\n"


1195  " * Imported names.\n"


1196  " */\n"


1197  "BEGINCONSTSTRINGS\n"


1198  "g_szLibrary:\n"


1199  " .asciz \"%s\"\n"


1200  "\n"


1201  "g_szzNames:\n",


1202  g_pszLibrary);


1203  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


1204  if (!pExp>fNoName)


1205  fprintf(pOutput, " g_sz%s:\n .asciz \"%s\"\n", pExp>pszExportedNm, pExp>pszExportedNm);


1206  else


1207  fprintf(pOutput, " g_sz%s:\n .asciz \"#%u\"\n", pExp>pszExportedNm, pExp>uOrdinal);


1208  fprintf(pOutput,


1209  "g_EndOfNames: .byte 0\n"


1210  "\n"


1211  "g_szFailLoadFmt: .asciz \"Lazy loader failed to load \\\"%%s\\\": %%Rrc\\n\"\n"


1212  "g_szFailResolveFmt: .asciz \"Lazy loader failed to resolve symbol \\\"%%s\\\" in \\\"%%s\\\": %%Rrc\\n\"\n"


1213  "ENDCONSTSTRINGS\n"


1214  "\n"


1215  "\n");


1216 


1217  /*


1218  * The per import lazy load code.


1219  */


1220  fprintf(pOutput,


1221  "/*\n"


1222  " * Lazy load+resolve stubs.\n"


1223  " */\n"


1224  "BEGINCODE\n"


1225  ".p2align 3\n");


1226  for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp>pNext)


1227  {


1228  if (!pExp>fNoName)


1229  fprintf(pOutput,


1230  "___LazyLoad___%s:\n"


1231  " adrp x9, PAGE(g_sz%s)\n"


1232  " add x9, x9, PAGEOFF(g_sz%s)\n"


1233  " adrp x10, PAGE(NAME(%s%s))\n"


1234  " add x10, x10, PAGEOFF(NAME(%s%s))\n"


1235  " mov x16, x30\n"


1236  " bl LazyLoadResolver\n"


1237  " mov x30, x16\n"


1238  , pExp>pszExportedNm,


1239  pExp>pszExportedNm, pExp>pszExportedNm,


1240  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm,


1241  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm);


1242  else


1243  fprintf(pOutput,


1244  "___LazyLoad___%s:\n"


1245  " movk w9, #%u\n"


1246  " adrp x10, PAGE(NAME(%s%s))\n"


1247  " add x10, x10, PAGEOFF(NAME(%s%s))\n"


1248  , pExp>pszExportedNm,


1249  pExp>uOrdinal,


1250  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm,


1251  !pExp>fData ? "g_pfn" : "g_LazyPtr_", pExp>pszExportedNm);


1252  if (!pExp>fData)


1253  fprintf(pOutput, " b NAME(%s)\n", pExp>szName);


1254  else


1255  fprintf(pOutput, " b NAME(LazyGetPtr_%s)\n", pExp>szName);


1256  fprintf(pOutput, "\n");


1257  }


1258  fprintf(pOutput,


1259  "ENDCODE\n"


1260  "\n"


1261  "\n"


1262  "\n");


1263 


1264  /*


1265  * The code that does the loading and resolving.


1266  */


1267  fprintf(pOutput,


1268  "/*\n"


1269  " * The module handle.\n"


1270  " */\n"


1271  "BEGINDATA\n"


1272  "g_hMod:\n"


1273  " .quad 0\n"


1274  "ENDDATA\n"


1275  "\n"


1276  "\n"


1277  "\n");


1278 


1279  /*


1280  * Common lazy loader and resolved.


1281  */


1282  fprintf(pOutput,


1283  "/*\n"


1284  " * The resolver code.\n"


1285  " */\n"


1286  "BEGINCODE\n"


1287  ".p2align 3\n"


1288  "LazyLoadResolver:\n"


1289  " .cfi_startproc\n"


1290  " /* Create frame. */\n"


1291  " sub sp, sp, #(16 + 192)\n"


1292  " stp x29, x30, [sp, #192]\n"


1293  " add x29, sp, #192\n"


1294  " .cfi_def_cfa x29, 16\n"


1295  " .cfi_offset x30, 8\n"


1296  " .cfi_offset x29, 16\n"


1297  " /* Save all argument registers and a handful of preserved ones. */\n"


1298  " stp x0, x1, [sp, #(192  16)]\n"


1299  " .cfi_offset x0, 32\n"


1300  " .cfi_offset x1, 24\n"


1301  " stp x2, x3, [sp, #(192  32)]\n"


1302  " .cfi_offset x3, 40\n"


1303  " .cfi_offset x2, 48\n"


1304  " stp x4, x5, [sp, #(192  48)]\n"


1305  " .cfi_offset x6, 56\n"


1306  " .cfi_offset x5, 64\n"


1307  " stp x6, x7, [sp, #(192  64)]\n"


1308  " .cfi_offset x7, 72\n"


1309  " .cfi_offset x6, 80\n"


1310  " stp x16, x17, [sp, #(192  80)]\n"


1311  " .cfi_offset x17, 88\n"


1312  " .cfi_offset x16, 96\n"


1313  " stp x18, x19, [sp, #(192  96)]\n"


1314  " .cfi_offset x19, 104\n"


1315  " .cfi_offset x18, 112\n"


1316  " stp x20, x21, [sp, #(192  112)]\n"


1317  " .cfi_offset x21, 120\n"


1318  " .cfi_offset x20, 128\n"


1319  " stp x22, x23, [sp, #(192  128)]\n"


1320  " .cfi_offset x23, 136\n"


1321  " .cfi_offset x22, 144\n"


1322  " str x8, [sp, #(192  144)]\n"


1323  "\n"


1324  " /* Shift the symbol name to x19 and g_pfnXXXX pointer to x20 as these are preserved registers\n"


1325  " * (in case we need to call LazyLoadModule/RTLdrLoad) */\n"


1326  " mov x19, x9\n"


1327  " mov x20, x10\n"


1328  "\n"


1329  " /* Get the module handle and call RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue) */\n"


1330  " adrp x0, PAGE(g_hMod)\n"


1331  " ldr x0, [x0, PAGEOFF(g_hMod)]\n"


1332  " cmp x0, #0\n"


1333  " b.ne Lloaded\n"


1334  " bl LazyLoading\n"


1335  "Lloaded:\n"


1336  " mov x1, x19\n"


1337  " mov x2, x20\n"


1338  " bl NAME(RTLdrGetSymbol)\n"


1339  "\n"


1340  " cmp w0, #0\n"


1341  " b.eq Lreturn\n"


1342  "\n"


1343  "Lbadsym: /* Call sRTAssertMsg2Weak. Variadic (...) arguments are passed on the stack it seems. */\n"


1344  " mov x3, x0\n"


1345  " adrp x2, PAGE(g_szLibrary)\n"


1346  " add x2, x2, PAGEOFF(g_szLibrary)\n"


1347  " mov x1, x19\n"


1348  " adrp x0, PAGE(g_szFailLoadFmt)\n"


1349  " add x0, x0, PAGEOFF(g_szFailLoadFmt)\n"


1350  " stp x1, x2, [sp]\n"


1351  " str x3, [sp, #16]\n"


1352  " bl NAME(RTAssertMsg2Weak)\n"


1353  "Lbadsymloop:\n"


1354  " brk #0x1\n"


1355  " b Lbadsymloop\n"


1356 


1357  "Lreturn:\n"


1358  " /* Restore saved register */\n"


1359  " ldr x8, [sp, #(192  144)]\n"


1360  " .cfi_restore x8\n"


1361  " ldp x22, x23, [sp, #(192  128)]\n"


1362  " .cfi_restore x23\n"


1363  " .cfi_restore x22\n"


1364  " ldp x20, x21, [sp, #(192  112)]\n"


1365  " .cfi_restore x21\n"


1366  " .cfi_restore x20\n"


1367  " ldp x18, x19, [sp, #(192  96)]\n"


1368  " .cfi_restore x19\n"


1369  " .cfi_restore x18\n"


1370  " ldp x16, x17, [sp, #(192  80)]\n"


1371  " .cfi_restore x17\n"


1372  " .cfi_restore x18\n"


1373  " ldp x6, x7, [sp, #(192  64)]\n"


1374  " .cfi_restore x7\n"


1375  " .cfi_restore x6\n"


1376  " ldp x4, x5, [sp, #(192  48)]\n"


1377  " .cfi_restore x5\n"


1378  " .cfi_restore x4\n"


1379  " ldp x2, x3, [sp, #(192  32)]\n"


1380  " .cfi_restore x3\n"


1381  " .cfi_restore x2\n"


1382  " ldp x0, x1, [sp, #(192  16)]\n"


1383  " .cfi_restore x1\n"


1384  " .cfi_restore x0\n"


1385  "\n"


1386  " ldp x29, x30, [sp, #192]\n"


1387  " .cfi_restore x29\n"


1388  " .cfi_restore x30\n"


1389  " add sp, sp, #(16 + 192)\n"


1390  " ret\n"


1391  " .cfi_endproc\n"


1392  "\n"


1393  "\n");


1394 


1395  fprintf(pOutput,


1396  "/*\n"


1397  " * Loads the module.\n"


1398  " * ASSUMES called from LazyLoadResolver where all relevant registers are already saved.\n"


1399  " */\n"


1400  "LazyLoading:\n"


1401  " .cfi_startproc\n"


1402  " /* Create frame. */\n"


1403  " sub sp, sp, #(16 + 48)\n"


1404  " stp x29, x30, [sp, #48]\n"


1405  " add x29, sp, #48\n"


1406  " .cfi_def_cfa x29, 16\n"


1407  " .cfi_offset x30, 8\n"


1408  " .cfi_offset x29, 16\n"


1409  "\n");


1410 


1411  if (!g_fSystemLibrary)


1412  fprintf(pOutput,


1413  " /* Call SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo); */\n"


1414  " mov x3, #0\n"


1415  " mov x2, #0\n"


1416  " adrp x1, PAGE(g_hMod)\n"


1417  " add x1, x1, PAGEOFF(g_hMod)\n"


1418  " adrp x0, PAGE(g_szLibrary)\n"


1419  " add x0, x0, PAGEOFF(g_szLibrary)\n"


1420  " bl NAME(SUPR3HardenedLdrLoadAppPriv)\n");


1421  else


1422  fprintf(pOutput,


1423  " /* Call RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod); */\n"


1424  " adrp x2, PAGE(g_hMod)\n"


1425  " add x2, x2, PAGEOFF(g_hMod)\n"


1426  " mov x1, #1\n"


1427  " adrp x0, PAGE(g_szLibrary)\n"


1428  " add x0, x0, PAGEOFF(g_szLibrary)\n"


1429  " bl NAME(RTLdrLoadSystem)\n");


1430 


1431  fprintf(pOutput,


1432  " cmp w0, #0\n"


1433  " b.eq Lload_return\n"


1434  "\n"


1435  "Lbadload: /* Call sRTAssertMsg2Weak. Variadic (...) arguments are passed on the stack it seems. */\n"


1436  " mov x2, x0\n"


1437  " adrp x1, PAGE(g_szLibrary)\n"


1438  " add x1, x1, PAGEOFF(g_szLibrary)\n"


1439  " adrp x0, PAGE(g_szFailResolveFmt)\n"


1440  " add x0, x0, PAGEOFF(g_szFailResolveFmt)\n"


1441  " stp x1, x2, [sp]\n"


1442  " bl NAME(RTAssertMsg2Weak)\n"


1443  "Lbadloadloop:\n"


1444  " brk #0x1\n"


1445  " b Lbadloadloop\n"


1446  "Lload_return:\n"


1447  " adrp x0, PAGE(g_hMod)\n"


1448  " ldr x0, [x0, PAGEOFF(g_hMod)]\n"


1449  " ldp x29, x30, [sp, #48]\n"


1450  " .cfi_restore x29\n"


1451  " .cfi_restore x30\n"


1452  " add sp, sp, #(16 + 48)\n"


1453  " ret\n"


1454  " .cfi_endproc\n"


1455  "ENDCODE\n"


1456  "\n"


1457  "\n");


1458 


1459  /*


1460  * C callable method for explicitly loading the library and optionally


1461  * resolving all the imports.


1462  */


1463  if (g_fWithExplictLoadFunction)


1464  {


1465  int cchLibBaseName = (int)(strchr(g_pszLibrary, '.') ? strchr(g_pszLibrary, '.')  g_pszLibrary : strlen(g_pszLibrary));


1466  fprintf(pOutput,


1467  "/**\n"


1468  " * ExplicitlyLoad%.*s(bool fResolveAllImports, pErrInfo);\n"


1469  " */\n"


1470  "BEGINCODE\n"


1471  ".p2align 3\n"


1472  ".globl NAME(ExplicitlyLoad%.*s)\n"


1473  "NAME(ExplicitlyLoad%.*s):\n"


1474  " .cfi_startproc\n"


1475  " /* Create frame. */\n"


1476  " sub sp, sp, #(16 + 96)\n"


1477  " stp x29, x30, [sp, #96]\n"


1478  " add x29, sp, #96\n"


1479  " .cfi_def_cfa x29, 16\n"


1480  " .cfi_offset x30, 8\n"


1481  " .cfi_offset x29, 16\n"


1482  "\n"


1483  " stp x20, x21, [sp, #(96  16)]\n"


1484  " .cfi_offset x21, 24\n"


1485  " .cfi_offset x20, 32\n"


1486  " stp x22, x23, [sp, #(96  32)]\n"


1487  " .cfi_offset x23, 40\n"


1488  " .cfi_offset x22, 48\n"


1489 


1490  " /* Save the input parameters. */\n"


1491  " mov x20, x0\n"


1492  " mov x21, x1\n"


1493  "\n"


1494  " /*\n"


1495  " * Is the module already loaded?\n"


1496  " */\n"


1497  " adrp x0, PAGE(g_hMod)\n"


1498  " ldr x0, [x0, PAGEOFF(g_hMod)]\n"


1499  " cmp x0, #0\n"


1500  " b.ne Lexplicit_loaded_module\n"


1501  "\n"


1502  ,


1503  cchLibBaseName, g_pszLibrary,


1504  cchLibBaseName, g_pszLibrary,


1505  cchLibBaseName, g_pszLibrary);


1506  fprintf(pOutput,


1507  "Lexplicit_load_module:\n");


1508  if (!g_fSystemLibrary)


1509  fprintf(pOutput,


1510  " /* Call SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo); */\n"


1511  " mov x3, x21\n"


1512  " mov x2, #0\n"


1513  " adrp x1, PAGE(g_hMod)\n"


1514  " add x1, x1, PAGEOFF(g_hMod)\n"


1515  " adrp x0, PAGE(g_szLibrary)\n"


1516  " add x0, x0, PAGEOFF(g_szLibrary)\n"


1517  " bl NAME(SUPR3HardenedLdrLoadAppPriv)\n");


1518  else


1519  fprintf(pOutput,


1520  " /* Call RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod); */\n"


1521  " adrp x2, PAGE(g_hMod)\n"


1522  " add x2, x2, PAGEOFF(g_hMod)\n"


1523  " mov x1, #1\n"


1524  " adrp x0, PAGE(g_szLibrary)\n"


1525  " add x0, x0, PAGEOFF(g_szLibrary)\n"


1526  " bl NAME(RTLdrLoadSystem)\n");


1527  fprintf(pOutput,


1528  " cmp x0, #0\n"


1529  " b.ne Lexplicit_load_return\n"


1530  "\n");


1531 


1532  fprintf(pOutput,


1533  " /*\n"


1534  " * Resolve the imports too if requested to do so.\n"


1535  " */\n"


1536  "Lexplicit_loaded_module:\n"


1537  " cmp w20, #0\n"


1538  " b.eq Lexplicit_load_return\n"


1539  "\n"


1540  " adrp x22, PAGE(g_szzNames)\n"


1541  " add x22, x22, PAGEOFF(g_szzNames)\n"


1542  " adrp x23, PAGE(g_apfnImports)\n"


1543  " add x23, x23, PAGEOFF(g_apfnImports)\n"


1544  "Lexplicit_load_next_import:\n"


1545  " ldr x0, [x23]\n"


1546  " cmp x0, #0\n"


1547  " b.eq Lexplicit_load_return\n"


1548  "\n"


1549  " /* Get the module handle and call RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue) */\n"


1550  " adrp x0, PAGE(g_hMod)\n"


1551  " ldr x0, [x0, PAGEOFF(g_hMod)]\n"


1552  " mov x1, x22\n"


1553  " mov x2, x23\n"


1554  " bl NAME(RTLdrGetSymbol)\n"


1555  " cmp x0, #0\n"


1556  " b.ne Lexplicit_load_symbol_error\n"


1557  "\n"


1558  " /* Advance. */\n"


1559  " add x23, x23, #8\n"


1560  "Lexplict_load_advance_string:\n"


1561  " ldrb w0, [x22]\n"


1562  " add x22, x22, #1\n"


1563  " cmp w0, #0\n"


1564  " b.ne Lexplict_load_advance_string\n"


1565  " b Lexplicit_load_next_import\n"


1566  "\n"


1567  " /*\n"


1568  " * Error loading a symbol. Call RTErrInfoSet(PRTERRINFO pErrInfo, int rc, const char *pszMsg) on pErrInfo (preserves x0).\n"


1569  " */\n"


1570  "Lexplicit_load_symbol_error:\n"


1571  " mov x2, x22\n"


1572  " mov x1, x0\n"


1573  " mov x0, x21\n"


1574  " bl NAME(RTErrInfoSet)\n"


1575  " b Lexplicit_load_return"


1576  " "


1577  "\n"


1578  "Lexplicit_load_return:\n"


1579  " ldp x22, x23, [sp, #(96  32)]\n"


1580  " .cfi_restore x23\n"


1581  " .cfi_restore x22\n"


1582  " ldp x20, x21, [sp, #(96  16)]\n"


1583  " .cfi_restore x21\n"


1584  " .cfi_restore x20\n"


1585  "\n"


1586  " ldp x29, x30, [sp, #96]\n"


1587  " .cfi_restore x29\n"


1588  " .cfi_restore x30\n"


1589  " add sp, sp, #(16 + 96)\n"


1590  " ret\n"


1591  " .cfi_endproc\n"


1592  "ENDCODE\n"


1593  "\n"


1594  "\n");


1595  }


1596 


1597  return RTEXITCODE_SUCCESS;


1598  }


1599 


1600 


1601  /**


1602  * Generates the assembly source code, writing it to g_pszOutput.


1603  *


1604  * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full


1605  * details has been displayed.


1606  */


1607  static RTEXITCODE generateOutput(void)


1608  {


1609  RTEXITCODE rcExit = RTEXITCODE_FAILURE;


1610  FILE *pOutput = fopen(g_pszOutput, "w");


1611  if (pOutput)


1612  {


1613  switch (g_enmTarget)


1614  {


1615  case RTLDRARCH_AMD64:


1616  case RTLDRARCH_X86_32:


1617  rcExit = generateOutputInnerX86AndAMD64(pOutput);


1618  break;


1619  case RTLDRARCH_ARM64:


1620  rcExit = generateOutputInnerArm64(pOutput);


1621  break;


1622  default:


1623  rcExit = RTEXITCODE_FAILURE;


1624  break;


1625  }


1626  if (fclose(pOutput))


1627  {


1628  fprintf(stderr, "error: Error closing '%s'.\n", g_pszOutput);


1629  rcExit = RTEXITCODE_FAILURE;


1630  }


1631  }


1632  else


1633  fprintf(stderr, "error: Failed to open '%s' for writing.\n", g_pszOutput);


1634  return rcExit;


1635  }


1636 


1637 


1638  /**


1639  * Displays usage information.


1640  *


1641  * @returns RTEXITCODE_SUCCESS.


1642  * @param pszArgv0 The argv[0] string.


1643  */


1644  static int usage(const char *pszArgv0)


1645  {


1646  const char *pszTmp = strrchr(pszArgv0, '/');


1647  if (pszTmp)


1648  pszArgv0 = pszTmp + 1;


1649  pszTmp = strrchr(pszArgv0, '\\');


1650  if (pszTmp)


1651  pszArgv0 = pszTmp + 1;


1652 


1653  /* 0 1 2 3 4 5 6 7 8


1654  012345678901234567890123456789012345678901234567890123456789012345678901234567890 */


1655  printf("VBoxDef2LazyLoad  Lazy DLL/SO/DYLIB loader code generator.\n"


1656  "Copyright (C) 20132016 Oracle Corporation\n"


1657  "\n"


1658  "Description:\n"


1659  "\n"


1660  "\n"


1661  "Takes a Microsoftstyle linker definition file for a library (DLL/SO/DYLIB) and\n"


1662  "generates assembly code which defines stub functions that lazily loads and\n"


1663  "resolves the real symbols before calling them. This is entirely transparent when\n"


1664  "used with functions.\n"


1665  "\n"


1666  "With data symbols it's more messy since the compiler will not invoke code when\n"


1667  "using them, but access them directly (ELF executables) or indirectly (ELF SOs,\n"


1668  "PE, ++). For data symbols use the DATA keyword after the symbol name in the\n"


1669  "deffile and modify the header definition from 'extern type symbol;' to:\n"


1670  "\n"


1671  " DECLASM(type *) LazyGetPtr_<symbol>(void);\n"


1672  " #define <symbol> (*LazyGetPtr_<symbol>())\n"


1673  "\n"


1674  "or, if using explictloadfunction this will work as well:\n"


1675  "\n"


1676  " extern type *g_LazyPtr_<symbol>;\n"


1677  " #define <symbol> (*g_LazyPtr_)\n"


1678  "\n"


1679  "Usage:\n"


1680  "\n"


1681  "%s [options] libary <loadname> output <lazyload.asm> <input.def>\n"


1682  "\n"


1683  "Options:\n"


1684  "\n"


1685  " library <loadname>, l <loadname>\n"


1686  " The name of the library. This is what will be passed to RTLdrLoadSystem\n"


1687  " or SUPR3HardenedLdrLoadAppPriv.\n"


1688  " output <filename>, o <filename>\n"


1689  " The assembly output file.\n"


1690  " explicitloadfunction, noexplicitloadfunction\n"


1691  " Whether to include the explicit load function:\n"


1692  " DECLASM(int) ExplicitlyLoad<basename>(bool fResolveAllImports, pErrInfo);\n"


1693  " The default is not to include it.\n"


1694  " system\n"


1695  " The library is a system DLL to be loaded using RTLdrLoadSystem.\n"


1696  " The default is to use SUPR3HardenedLdrLoadAppPriv to load it.\n"


1697  "\n"


1698  , pszArgv0);


1699 


1700  return RTEXITCODE_SUCCESS;


1701  }


1702 


1703 


1704  int main(int argc, char **argv)


1705  {


1706  /*


1707  * Parse options.


1708  */


1709  for (int i = 1; i < argc; i++)


1710  {


1711  const char *psz = argv[i];


1712  if (*psz == '')


1713  {


1714  if (!strcmp(psz, "output")  !strcmp(psz, "o"))


1715  {


1716  if (++i >= argc)


1717  {


1718  fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz);


1719  return RTEXITCODE_SYNTAX;


1720  }


1721  g_pszOutput = argv[i];


1722  }


1723  else if (!strcmp(psz, "library")  !strcmp(psz, "l"))


1724  {


1725  if (++i >= argc)


1726  {


1727  fprintf(stderr, "syntax error: Library name expected after '%s'.\n", psz);


1728  return RTEXITCODE_SYNTAX;


1729  }


1730  g_pszLibrary = argv[i];


1731  }


1732  else if (!strcmp(psz, "explicitloadfunction"))


1733  g_fWithExplictLoadFunction = true;


1734  else if (!strcmp(psz, "noexplicitloadfunction"))


1735  g_fWithExplictLoadFunction = false;


1736  else if (!strcmp(psz, "system"))


1737  g_fSystemLibrary = true;


1738  /** @todo Support different load methods so this can be used on system libs and


1739  * such if we like. */


1740  else if ( !strcmp(psz, "help")


1741   !strcmp(psz, "help")


1742   !strcmp(psz, "h")


1743   !strcmp(psz, "?") )


1744  return usage(argv[0]);


1745  else if ( !strcmp(psz, "version")


1746   !strcmp(psz, "V"))


1747  {


1748  printf("$Revision: 105813 $\n");


1749  return RTEXITCODE_SUCCESS;


1750  }


1751  else


1752  {


1753  fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz);


1754  return RTEXITCODE_SYNTAX;


1755  }


1756  }


1757  else


1758  {


1759  if (g_cInputs >= RT_ELEMENTS(g_apszInputs))


1760  {


1761  fprintf(stderr, "syntax error: Too many input files, max is %d.\n", (int)RT_ELEMENTS(g_apszInputs));


1762  return RTEXITCODE_SYNTAX;


1763  }


1764  g_apszInputs[g_cInputs++] = argv[i];


1765  }


1766  }


1767  if (g_cInputs == 0)


1768  {


1769  fprintf(stderr, "syntax error: No input file specified.\n");


1770  return RTEXITCODE_SYNTAX;


1771  }


1772  if (!g_pszOutput)


1773  {


1774  fprintf(stderr, "syntax error: No output file specified.\n");


1775  return RTEXITCODE_SYNTAX;


1776  }


1777  if (!g_pszLibrary)


1778  {


1779  fprintf(stderr, "syntax error: No library name specified.\n");


1780  return RTEXITCODE_SYNTAX;


1781  }


1782 


1783  /*


1784  * Do the job.


1785  */


1786  RTEXITCODE rcExit = parseInputs();


1787  if (rcExit == RTEXITCODE_SUCCESS)


1788  rcExit = generateOutput();


1789  return rcExit;


1790  }


1791 

