1  /* $Id: VBoxDef2LazyLoad.cpp 76553 20190101 01:45:53Z 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) 20132019 Oracle Corporation


10  *


11  * This file is part of VirtualBox Open Source Edition (OSE), as


12  * available from http://www.virtualbox.org. This file is free software;


13  * you can redistribute it and/or modify it under the terms of the GNU


14  * General Public License (GPL) as published by the Free Software


15  * Foundation, in version 2 as it comes in the "COPYING" file of the


16  * VirtualBox OSE distribution. VirtualBox OSE is distributed in the


17  * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.


18  */


19 


20 


21  /*********************************************************************************************************************************


22  * Header Files *


23  *********************************************************************************************************************************/


24  #include <ctype.h>


25  #include <stdio.h>


26  #include <string.h>


27  #include <stdlib.h>


28  #include <iprt/types.h>


29 


30 


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


32  * Structures and Typedefs *


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


34  typedef struct MYEXPORT


35  {


36  struct MYEXPORT *pNext;


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


38  char *pszUnstdcallName;


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


40  char const *pszExportedNm;


41  unsigned uOrdinal;


42  bool fNoName;


43  char szName[1];


44  } MYEXPORT;


45  typedef MYEXPORT *PMYEXPORT;


46 


47 


48 


49  /*********************************************************************************************************************************


50  * Global Variables *


51  *********************************************************************************************************************************/


52  /** @name Options


53  * @{ */


54  static const char *g_pszOutput = NULL;


55  static const char *g_pszLibrary = NULL;


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


57  static unsigned g_cInputs = 0;


58  static bool g_fIgnoreData = true;


59  static bool g_fWithExplictLoadFunction = false;


60  static bool g_fSystemLibrary = false;


61  /** @} */


62 


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


64  static PMYEXPORT g_pExpHead = NULL;


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


66  static PMYEXPORT *g_ppExpNext = &g_pExpHead;


67 


68 


69 


70  #if 0 /* unused */


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


72  {


73  while (isspace(*psz))


74  psz++;


75  return psz;


76  }


77  #endif


78 


79 


80  static char *leftStrip(char *psz)


81  {


82  while (isspace(*psz))


83  psz++;


84  return psz;


85  }


86 


87 


88  static unsigned wordLength(const char *pszWord)


89  {


90  unsigned off = 0;


91  char ch;


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


93  && ch != '='


94  && ch != ','


95  && ch != ':'


96  && !isspace(ch) )


97  off++;


98  return off;


99  }


100 


101 


102  /**


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


104  *


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


106  * details has been displayed.


107  * @param pInput The input stream.


108  */


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


110  {


111  /*


112  * Process the file linebyline.


113  */


114  bool fInExports = false;


115  unsigned iLine = 0;


116  char szLine[16384];


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


118  {


119  iLine++;


120 


121  /*


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


123  * trailing comments.


124  */


125  char *psz = leftStrip(szLine);


126  if (*psz == ';')


127  continue; /* comment line. */


128 


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


130  if (pszComment)


131  *pszComment = '\0';


132 


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


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


135  psz[cch] = '\0';


136 


137  if (!cch)


138  continue;


139 


140  /*


141  * Check for known directives.


142  */


143  size_t cchWord0 = wordLength(psz);


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


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


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


147  {


148  fInExports = true;


149 


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


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


152  if (!*psz)


153  continue;


154  }


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


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


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


158   WORD_CMP(psz, cchWord0, "LIBRARY")


159   WORD_CMP(psz, cchWord0, "DESCRIPTION")


160   WORD_CMP(psz, cchWord0, "STACKSIZE")


161   WORD_CMP(psz, cchWord0, "SECTIONS")


162   WORD_CMP(psz, cchWord0, "SEGMENTS")


163   WORD_CMP(psz, cchWord0, "VERSION")


164  )


165  {


166  fInExports = false;


167  }


168 


169  /*


170  * Process exports:


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


172  */


173  if (fInExports)


174  {


175  const char *pchName = psz;


176  unsigned cchName = wordLength(psz);


177 


178  psz = leftStrip(psz + cchName);


179  if (*psz == '=')


180  {


181  psz = leftStrip(psz + 1);


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


183  }


184 


185  bool fNoName = false;


186  unsigned uOrdinal = ~0U;


187  if (*psz == '@')


188  {


189  psz++;


190  if (!isdigit(*psz))


191  {


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


193  return RTEXITCODE_FAILURE;


194  }


195  uOrdinal = *psz++  '0';


196  while (isdigit(*psz))


197  {


198  uOrdinal *= 10;


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


200  }


201  psz = leftStrip(psz);


202  cch = wordLength(psz);


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


204  {


205  fNoName = true;


206  psz = leftStrip(psz + cch);


207  }


208  }


209 


210  while (*psz)


211  {


212  cch = wordLength(psz);


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


214  {


215  if (!g_fIgnoreData)


216  {


217  fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n",


218  pszInput, iLine, cchName, pchName);


219  return RTEXITCODE_SUCCESS;


220  }


221  }


222  else if (!WORD_CMP(psz, cch, "PRIVATE"))


223  {


224  fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n",


225  pszInput, iLine, cchName, pchName);


226  return RTEXITCODE_SUCCESS;


227  }


228  psz = leftStrip(psz + cch);


229  }


230 


231  /*


232  * Check for stdcall mangling.


233  */


234  size_t cbExp = sizeof(MYEXPORT) + cchName;


235  unsigned cchStdcall = 0;


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


237  {


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


239  cchStdcall = 2;


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


241  cchStdcall = 3;


242  if (cchStdcall)


243  cbExp += cchName  1  cchStdcall;


244  }


245 


246  /*


247  * Add the export.


248  */


249 


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


251  if (!pExp)


252  {


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


254  return RTEXITCODE_SUCCESS;


255  }


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


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


258  if (!cchStdcall)


259  {


260  pExp>pszUnstdcallName = NULL;


261  pExp>pszExportedNm = pExp>szName;


262  }


263  else


264  {


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


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


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


268  pExp>pszExportedNm = pExp>pszUnstdcallName;


269  }


270  pExp>uOrdinal = uOrdinal;


271  pExp>fNoName = fNoName;


272  pExp>pNext = NULL;


273  *g_ppExpNext = pExp;


274  g_ppExpNext = &pExp>pNext;


275  }


276  }


277 


278  /*


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


280  */


281  if (feof(pInput))


282  return RTEXITCODE_SUCCESS;


283  fprintf(stderr, "error: Read while reading '%s' (iLine=%u).\n", pszInput, iLine);


284  return RTEXITCODE_FAILURE;


285  }


286 


287 


288  /**


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


290  *


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


292  * details has been displayed.


293  */


294  static RTEXITCODE parseInputs(void)


295  {


296  RTEXITCODE rcExit = RTEXITCODE_SUCCESS;


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


298  {


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


300  if (pInput)


301  {


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


303  fclose(pInput);


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


305  {


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


307  rcExit2 = RTEXITCODE_FAILURE;


308  }


309  if (rcExit2 != RTEXITCODE_SUCCESS)


310  rcExit = rcExit2;


311  }


312  else


313  {


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


315  rcExit = RTEXITCODE_FAILURE;


316  }


317  }


318  return rcExit;


319  }


320 


321 


322  /**


323  * Generates the assembly source code, writing it to @a pOutput.


324  *


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


326  * details has been displayed.


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


328  * when closing).


329  */


330  static RTEXITCODE generateOutputInner(FILE *pOutput)


331  {


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


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


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


335 


336  fprintf(pOutput,


337  ";; DO NOT EDIT!\n"


338  ";;\n"


339  "\n"


340  "\n"


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


342  "\n"


343  "\n");


344 


345  /*


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


347  */


348  fprintf(pOutput,


349  ";\n"


350  "; Thunks.\n"


351  ";\n"


352  "BEGINCODE\n");


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


354  if (!pExp>pszUnstdcallName)


355  fprintf(pOutput,


356  "BEGINPROC %s\n"


357  " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"


358  "ENDPROC %s\n",


359  pExp>szName, pExp>szName, pExp>szName);


360  else


361  fprintf(pOutput,


362  "%%ifdef RT_ARCH_X86\n"


363  "global %s\n"


364  "%s:\n"


365  " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"


366  "%%else\n"


367  "BEGINPROC %s\n"


368  " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"


369  "ENDPROC %s\n"


370  "%%endif\n",


371  pExp>szName, pExp>szName, pExp>pszUnstdcallName,


372  pExp>pszUnstdcallName, pExp>pszUnstdcallName, pExp>pszUnstdcallName);


373 


374  fprintf(pOutput,


375  "\n"


376  "\n");


377 


378  /*


379  * Import pointers


380  */


381  fprintf(pOutput,


382  ";\n"


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


384  ";\n"


385  "BEGINDATA\n"


386  "g_apfnImports:\n");


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


388  if (pExp>pszUnstdcallName)


389  fprintf(pOutput,


390  "%%ifdef ASM_FORMAT_PE\n"


391  " %%ifdef RT_ARCH_X86\n"


392  "global __imp_%s\n"


393  "__imp_%s:\n"


394  " %%else\n"


395  "global __imp_%s\n"


396  "__imp_%s:\n"


397  " %%endif\n"


398  "%%endif\n"


399  "g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n"


400  "\n",


401  pExp>szName,


402  pExp>szName,


403  pExp>pszUnstdcallName,


404  pExp>pszUnstdcallName,


405  pExp>pszExportedNm,


406  pExp>pszExportedNm);


407  else


408  fprintf(pOutput,


409  "%%ifdef ASM_FORMAT_PE\n"


410  "global __imp_%s\n"


411  "__imp_%s:\n"


412  "%%endif\n"


413  "g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n"


414  "\n",


415  pExp>szName,


416  pExp>szName,


417  pExp>pszExportedNm,


418  pExp>pszExportedNm);


419  fprintf(pOutput,


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


421  "\n"


422  "\n");


423 


424  /*


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


426  *


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


428  * g_apfnImports in the loadeverything routine further down.


429  */


430  fprintf(pOutput,


431  ";\n"


432  "; Imported names.\n"


433  ";\n"


434  "BEGINCODE\n"


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


436  "\n"


437  "g_szzNames:\n",


438  g_pszLibrary);


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


440  if (!pExp>fNoName)


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


442  else


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


444  fprintf(pOutput,


445  "g_EndOfNames: db 0\n"


446  "\n"


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


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


449  "\n"


450  "\n");


451 


452  /*


453  * The per import lazy load code.


454  */


455  fprintf(pOutput,


456  ";\n"


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


458  ";\n"


459  "BEGINCODE\n");


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


461  {


462  if (!pExp>fNoName)


463  fprintf(pOutput,


464  "___LazyLoad___%s:\n"


465  /* "int3\n" */


466  "%%ifdef RT_ARCH_AMD64\n"


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


468  " lea r10, [g_pfn%s wrt rip]\n"


469  " call LazyLoadResolver\n"


470  "%%elifdef RT_ARCH_X86\n"


471  " push g_sz%s\n"


472  " push g_pfn%s\n"


473  " call LazyLoadResolver\n"


474  " add esp, 8h\n"


475  "%%else\n"


476  " %%error \"Unsupported architecture\"\n"


477  "%%endif\n"


478  ,


479  pExp>pszExportedNm,


480  pExp>pszExportedNm,


481  pExp>pszExportedNm,


482  pExp>pszExportedNm,


483  pExp>pszExportedNm);


484  else


485  fprintf(pOutput,


486  "___LazyLoad___%s:\n"


487  /* "int3\n" */


488  "%%ifdef RT_ARCH_AMD64\n"


489  " mov eax, %u\n"


490  " lea r10, [g_pfn%s wrt rip]\n"


491  " call LazyLoadResolver\n"


492  "%%elifdef RT_ARCH_X86\n"


493  " push %u\n"


494  " push g_pfn%s\n"


495  " call LazyLoadResolver\n"


496  " add esp, 8h\n"


497  "%%else\n"


498  " %%error \"Unsupported architecture\"\n"


499  "%%endif\n"


500  ,


501  pExp>pszExportedNm,


502  pExp>uOrdinal,


503  pExp>pszExportedNm,


504  pExp>uOrdinal,


505  pExp>pszExportedNm);


506  if (!pExp>pszUnstdcallName)


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


508  else


509  fprintf(pOutput,


510  "%%ifdef RT_ARCH_X86\n"


511  " jmp %s\n"


512  "%%else\n"


513  " jmp NAME(%s)\n"


514  "%%endif\n"


515  ,


516  pExp>szName, pExp>pszUnstdcallName);


517  fprintf(pOutput, "\n");


518  }


519  fprintf(pOutput,


520  "\n"


521  "\n"


522  "\n");


523 


524  /*


525  * The code that does the loading and resolving.


526  */


527  fprintf(pOutput,


528  ";\n"


529  "; The module handle.\n"


530  ";\n"


531  "BEGINDATA\n"


532  "g_hMod RTCCPTR_DEF 0\n"


533  "\n"


534  "\n"


535  "\n");


536 


537  /*


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


539  *


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


541  * saved all necessary registers.


542  */


543  if (!g_fSystemLibrary)


544  fprintf(pOutput,


545  ";\n"


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


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


548  ";\n"


549  "EXTERN_IMP2 SUPR3HardenedLdrLoadAppPriv\n"


550  "%%ifdef IN_RT_R3\n"


551  "extern NAME(RTAssertMsg2Weak)\n"


552  "%%else\n"


553  "EXTERN_IMP2 RTAssertMsg2Weak\n"


554  "%%endif\n"


555  "BEGINCODE\n"


556  "\n"


557  "LazyLoading:\n"


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


559  " or xCX, xCX\n"


560  " jnz .return\n"


561  "\n"


562  "%%ifdef ASM_CALL64_GCC\n"


563  " xor rcx, rcx ; pErrInfo\n"


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


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


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


567  " sub rsp, 08h\n"


568  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


569  " add rsp, 08h\n"


570  "\n"


571  "%%elifdef ASM_CALL64_MSC\n"


572  " xor r9, r9 ; pErrInfo\n"


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


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


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


576  " sub rsp, 28h\n"


577  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


578  " add rsp, 28h\n"


579  "\n"


580  "%%elifdef RT_ARCH_X86\n"


581  " sub xSP, 0ch\n"


582  " push 0 ; pErrInfo\n"


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


584  " push g_hMod ; phLdrMod\n"


585  " push g_szLibrary ; pszFilename\n"


586  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


587  " add esp, 1ch\n"


588  "%%else\n"


589  " %%error \"Unsupported architecture\"\n"


590  "%%endif\n");


591  else


592  fprintf(pOutput,


593  ";\n"


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


595  ";\n"


596  "%%ifdef IN_RT_R3\n"


597  "extern NAME(RTLdrLoadSystem)\n"


598  "extern NAME(RTAssertMsg2Weak)\n"


599  "%%else\n"


600  "EXTERN_IMP2 RTLdrLoadSystem\n"


601  "EXTERN_IMP2 RTAssertMsg2Weak\n"


602  "%%endif\n"


603  "BEGINCODE\n"


604  "\n"


605  "LazyLoading:\n"


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


607  " or xCX, xCX\n"


608  " jnz .return\n"


609  "\n"


610  "%%ifdef ASM_CALL64_GCC\n"


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


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


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


614  " sub rsp, 08h\n"


615  " %%ifdef IN_RT_R3\n"


616  " call NAME(RTLdrLoadSystem)\n"


617  " %%else\n"


618  " call IMP2(RTLdrLoadSystem)\n"


619  " %%endif\n"


620  " add rsp, 08h\n"


621  "\n"


622  "%%elifdef ASM_CALL64_MSC\n"


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


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


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


626  " sub rsp, 28h\n"


627  " %%ifdef IN_RT_R3\n"


628  " call NAME(RTLdrLoadSystem)\n"


629  " %%else\n"


630  " call IMP2(RTLdrLoadSystem)\n"


631  " %%endif\n"


632  " add rsp, 28h\n"


633  "\n"


634  "%%elifdef RT_ARCH_X86\n"


635  " push g_hMod ; phLdrMod\n"


636  " push 1 ; fNoUnload=true\n"


637  " push g_szLibrary ; pszFilename\n"


638  " %%ifdef IN_RT_R3\n"


639  " call NAME(RTLdrLoadSystem)\n"


640  " %%else\n"


641  " call IMP2(RTLdrLoadSystem)\n"


642  " %%endif\n"


643  " add esp, 0ch\n"


644  "%%else\n"


645  " %%error \"Unsupported architecture\"\n"


646  "%%endif\n");


647  fprintf(pOutput,


648  " or eax, eax\n"


649  " jnz .badload\n"


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


651  ".return:\n"


652  " ret\n"


653  "\n"


654  ".badload:\n"


655  "%%ifdef ASM_CALL64_GCC\n"


656  " mov edx, eax\n"


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


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


659  " sub rsp, 08h\n"


660  "%%elifdef ASM_CALL64_MSC\n"


661  " mov r8d, eax\n"


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


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


664  " sub rsp, 28h\n"


665  "%%elifdef RT_ARCH_X86\n"


666  " push eax\n"


667  " push g_szLibrary\n"


668  " push g_szFailLoadFmt\n"


669  "%%endif\n"


670  "%%ifdef IN_RT_R3\n"


671  " call NAME(RTAssertMsg2Weak)\n"


672  "%%else\n"


673  " call IMP2(RTAssertMsg2Weak)\n"


674  "%%endif\n"


675  ".badloadloop:\n"


676  " int3\n"


677  " jmp .badloadloop\n"


678  "LazyLoading_End:\n"


679  "\n"


680  "\n");


681 


682 


683  fprintf(pOutput,


684  ";\n"


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


686  ";\n"


687  "%%ifdef IN_RT_R3\n"


688  "extern NAME(RTLdrGetSymbol)\n"


689  "%%else\n"


690  "EXTERN_IMP2 RTLdrGetSymbol\n"


691  "%%endif\n"


692  "BEGINCODE\n"


693  "LazyLoadResolver:\n"


694  "%%ifdef RT_ARCH_AMD64\n"


695  " push rbp\n"


696  " mov rbp, rsp\n"


697  " push r15\n"


698  " push r14\n"


699  " mov r15, rax ; name\n"


700  " mov r14, r10 ; ppfn\n"


701  " push r9\n"


702  " push r8\n"


703  " push rcx\n"


704  " push rdx\n"


705  " push r12\n"


706  " %%ifdef ASM_CALL64_GCC\n"


707  " push rsi\n"


708  " push rdi\n"


709  " mov r12, rsp\n"


710  " %%else\n"


711  " mov r12, rsp\n"


712  " sub rsp, 20h\n"


713  " %%endif\n"


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


715  "\n"


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


717  " %%ifdef ASM_CALL64_GCC\n"


718  " mov rdi, rcx ; hLdrMod\n"


719  " mov rsi, r15 ; pszSymbol\n"


720  " mov rdx, r14 ; ppvValue\n"


721  " %%else\n"


722  " mov rdx, r15 ; pszSymbol\n"


723  " mov r8, r14 ; ppvValue\n"


724  " %%endif\n"


725  " %%ifdef IN_RT_R3\n"


726  " call NAME(RTLdrGetSymbol)\n"


727  " %%else\n"


728  " call IMP2(RTLdrGetSymbol)\n"


729  " %%endif\n"


730  " or eax, eax\n"


731  " jnz .badsym\n"


732  "\n"


733  " mov rsp, r12\n"


734  " %%ifdef ASM_CALL64_GCC\n"


735  " pop rdi\n"


736  " pop rsi\n"


737  " %%endif\n"


738  " pop r12\n"


739  " pop rdx\n"


740  " pop rcx\n"


741  " pop r8\n"


742  " pop r9\n"


743  " pop r14\n"


744  " pop r15\n"


745  " leave\n"


746  "\n"


747  "%%elifdef RT_ARCH_X86\n"


748  " push ebp\n"


749  " mov ebp, esp\n"


750  " push eax\n"


751  " push ecx\n"


752  " push edx\n"


753  " and esp, 0fffffff0h\n"


754  "\n"


755  ".loaded:\n"


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


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


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


759  " push ecx\n"


760  " %%ifdef IN_RT_R3\n"


761  " call NAME(RTLdrGetSymbol)\n"


762  " %%else\n"


763  " call IMP2(RTLdrGetSymbol)\n"


764  " %%endif\n"


765  " or eax, eax\n"


766  " jnz .badsym\n"


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


768  " pop edx\n"


769  " pop ecx\n"


770  " pop eax\n"


771  " leave\n"


772  "%%else\n"


773  " %%error \"Unsupported architecture\"\n"


774  "%%endif\n"


775  " ret\n"


776  "\n"


777  ".badsym:\n"


778  "%%ifdef ASM_CALL64_GCC\n"


779  " mov ecx, eax\n"


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


781  " mov rsi, r15\n"


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


783  " sub rsp, 08h\n"


784  "%%elifdef ASM_CALL64_MSC\n"


785  " mov r9d, eax\n"


786  " mov r8, r15\n"


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


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


789  " sub rsp, 28h\n"


790  "%%elifdef RT_ARCH_X86\n"


791  " push eax\n"


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


793  " push g_szLibrary\n"


794  " push g_szFailResolveFmt\n"


795  "%%endif\n"


796  "%%ifdef IN_RT_R3\n"


797  " call NAME(RTAssertMsg2Weak)\n"


798  "%%else\n"


799  " call IMP2(RTAssertMsg2Weak)\n"


800  "%%endif\n"


801  ".badsymloop:\n"


802  " int3\n"


803  " jmp .badsymloop\n"


804  "\n"


805  "LazyLoadResolver_End:\n"


806  "\n"


807  "\n"


808  );


809 


810 


811 


812  /*


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


814  * resolving all the imports.


815  */


816  if (g_fWithExplictLoadFunction)


817  {


818  if (g_fSystemLibrary) /* Lazy bird. */


819  {


820  fprintf(stderr, "error: cannot use system with explicitloadfunction, sorry\n");


821  return RTEXITCODE_FAILURE;


822  }


823 


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


825  fprintf(pOutput,


826  ";;\n"


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


828  ";\n"


829  "EXTERN_IMP2 RTErrInfoSet\n"


830  "BEGINCODE\n"


831  "BEGINPROC ExplicitlyLoad%.*s\n"


832  " push xBP\n"


833  " mov xBP, xSP\n"


834  " push xBX\n"


835  "%%ifdef ASM_CALL64_GCC\n"


836  " %%define pszCurStr r14\n"


837  " push r14\n"


838  "%%else\n"


839  " %%define pszCurStr xDI\n"


840  " push xDI\n"


841  "%%endif\n"


842  " sub xSP, 40h\n"


843  "\n"


844  " ;\n"


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


846  " ;\n"


847  "%%ifdef ASM_CALL64_GCC\n"


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


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


850  "%%elifdef ASM_CALL64_MSC\n"


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


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


853  "%%endif\n"


854  "\n"


855  " ;\n"


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


857  " ;\n"


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


859  " jnz .loaded\n"


860  "\n"


861  " ;\n"


862  " ; Load the module.\n"


863  " ;\n"


864  "%%ifdef ASM_CALL64_GCC\n"


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


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


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


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


869  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


870  "\n"


871  "%%elifdef ASM_CALL64_MSC\n"


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


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


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


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


876  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


877  "\n"


878  "%%elifdef RT_ARCH_X86\n"


879  " sub xSP, 0ch\n"


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


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


882  " push g_hMod ; phLdrMod\n"


883  " push g_szLibrary ; pszFilename\n"


884  " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"


885  " add esp, 1ch\n"


886  "%%else\n"


887  " %%error \"Unsupported architecture\"\n"


888  "%%endif\n"


889  " or eax, eax\n"


890  " jnz .return\n"


891  "\n"


892  " ;\n"


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


894  " ;\n"


895  ".loaded:\n"


896  "%%ifdef ASM_ARCH_X86\n"


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


898  "%%else\n"


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


900  "%%endif\n"


901  " je .return\n"


902  "\n"


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


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


905  ".next_import:\n"


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


907  " je .return\n"


908  "%%ifdef ASM_CALL64_GCC\n"


909  " mov rdx, xBX ; ppvValue\n"


910  " mov rsi, pszCurStr ; pszSymbol\n"


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


912  " call IMP2(RTLdrGetSymbol)\n"


913  "%%elifdef ASM_CALL64_MSC\n"


914  " mov r8, xBX ; ppvValue\n"


915  " mov rdx, pszCurStr ; pszSymbol\n"


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


917  " call IMP2(RTLdrGetSymbol)\n"


918  "%%else\n"


919  " push xBX ; ppvValue\n"


920  " push pszCurStr ; pszSymbol\n"


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


922  " call IMP2(RTLdrGetSymbol)\n"


923  " add xSP, 0ch\n"


924  "%%endif\n"


925  " or eax, eax\n"


926  " jnz .symbol_error\n"


927  "\n"


928  " ; Advance.\n"


929  " add xBX, RTCCPTR_CB\n"


930  " xor eax, eax\n"


931  " mov xCX, 0ffffffffh\n"


932  "%%ifdef ASM_CALL64_GCC\n"


933  " mov xDI, pszCurStr\n"


934  " repne scasb\n"


935  " mov pszCurStr, xDI\n"


936  "%%else\n"


937  " repne scasb\n"


938  "%%endif\n"


939  " jmp .next_import\n"


940  "\n"


941  " ;\n"


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


943  " ;\n"


944  ".symbol_error:\n"


945  "%%ifdef ASM_CALL64_GCC\n"


946  " mov rdx, pszCurStr ; pszMsg\n"


947  " mov esi, eax ; rc\n"


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


949  " call IMP2(RTErrInfoSet)\n"


950  "%%elifdef ASM_CALL64_MSC\n"


951  " mov r8, pszCurStr ; pszMsg\n"


952  " mov edx, eax ; rc\n"


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


954  " call IMP2(RTErrInfoSet)\n"


955  "%%else\n"


956  " push pszCurStr ; pszMsg\n"


957  " push eax ; pszSymbol\n"


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


959  " call IMP2(RTErrInfoSet)\n"


960  " add xSP, 0ch\n"


961  "%%endif\n"


962  " "


963  "\n"


964  ".return:\n"


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


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


967  " leave\n"


968  " ret\n"


969  "ENDPROC ExplicitlyLoad%.*s\n"


970  "\n"


971  "\n"


972  ,


973  cchLibBaseName, g_pszLibrary,


974  cchLibBaseName, g_pszLibrary,


975  cchLibBaseName, g_pszLibrary


976  );


977  }


978 


979 


980  return RTEXITCODE_SUCCESS;


981  }


982 


983 


984  /**


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


986  *


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


988  * details has been displayed.


989  */


990  static RTEXITCODE generateOutput(void)


991  {


992  RTEXITCODE rcExit = RTEXITCODE_FAILURE;


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


994  if (pOutput)


995  {


996  rcExit = generateOutputInner(pOutput);


997  if (fclose(pOutput))


998  {


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


1000  rcExit = RTEXITCODE_FAILURE;


1001  }


1002  }


1003  else


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


1005  return rcExit;


1006  }


1007 


1008 


1009  /**


1010  * Displays usage information.


1011  *


1012  * @returns RTEXITCODE_SUCCESS.


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


1014  */


1015  static int usage(const char *pszArgv0)


1016  {


1017  printf("usage: %s [options] libary <loadname> output <lazyload.asm> <input.def>\n"


1018  "\n"


1019  "Options:\n"


1020  " explicitloadfunction, noexplicitloadfunction\n"


1021  " Whether to include the explicit load function, default is not to.\n"


1022  "\n"


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


1024  , pszArgv0);


1025 


1026  return RTEXITCODE_SUCCESS;


1027  }


1028 


1029 


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


1031  {


1032  /*


1033  * Parse options.


1034  */


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


1036  {


1037  const char *psz = argv[i];


1038  if (*psz == '')


1039  {


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


1041  {


1042  if (++i >= argc)


1043  {


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


1045  return RTEXITCODE_SYNTAX;


1046  }


1047  g_pszOutput = argv[i];


1048  }


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


1050  {


1051  if (++i >= argc)


1052  {


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


1054  return RTEXITCODE_SYNTAX;


1055  }


1056  g_pszLibrary = argv[i];


1057  }


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


1059  g_fWithExplictLoadFunction = true;


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


1061  g_fWithExplictLoadFunction = false;


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


1063  g_fSystemLibrary = true;


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


1065  * such if we like. */


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


1067   !strcmp(psz, "help")


1068   !strcmp(psz, "h")


1069   !strcmp(psz, "?") )


1070  return usage(argv[0]);


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


1072   !strcmp(psz, "V"))


1073  {


1074  printf("$Revision: 76553 $\n");


1075  return RTEXITCODE_SUCCESS;


1076  }


1077  else


1078  {


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


1080  return RTEXITCODE_SYNTAX;


1081  }


1082  }


1083  else


1084  {


1085  if (g_cInputs >= RT_ELEMENTS(g_apszInputs))


1086  {


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


1088  return RTEXITCODE_SYNTAX;


1089  }


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


1091  }


1092  }


1093  if (g_cInputs == 0)


1094  {


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


1096  return RTEXITCODE_SYNTAX;


1097  }


1098  if (!g_pszOutput)


1099  {


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


1101  return RTEXITCODE_SYNTAX;


1102  }


1103  if (!g_pszLibrary)


1104  {


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


1106  return RTEXITCODE_SYNTAX;


1107  }


1108 


1109  /*


1110  * Do the job.


1111  */


1112  RTEXITCODE rcExit = parseInputs();


1113  if (rcExit == RTEXITCODE_SUCCESS)


1114  rcExit = generateOutput();


1115  return rcExit;


1116  }


1117 

