VirtualBox

source: vbox/trunk/src/bldprogs/VBoxDef2LazyLoad.cpp@ 46475

Last change on this file since 46475 was 46475, checked in by vboxsync, 11 years ago

sans debug noise, pluss docs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.6 KB
Line 
1/* $Id: VBoxDef2LazyLoad.cpp 46475 2013-06-10 16:08:43Z vboxsync $ */
2/** @file
3 * VBoxDef2LazyLoad - Lazy Library Loader Generator.
4 *
5 * @note Only tested on win.amd64.
6 */
7
8/*
9 * Copyright (C) 2013 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* Header Files *
22*******************************************************************************/
23#include <ctype.h>
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <iprt/types.h>
28
29
30/*******************************************************************************
31* Structures and Typedefs *
32*******************************************************************************/
33typedef struct MYEXPORT
34{
35 struct MYEXPORT *pNext;
36 bool fNoName;
37 unsigned uOrdinal;
38 char szName[1];
39} MYEXPORT;
40typedef MYEXPORT *PMYEXPORT;
41
42
43
44/*******************************************************************************
45* Global Variables *
46*******************************************************************************/
47/** @name Options
48 * @{ */
49static const char *g_pszOutput = NULL;
50static const char *g_pszInput = NULL;
51static const char *g_pszLibrary = NULL;
52static bool g_fIgnoreData = true;
53/** @} */
54
55/** Pointer to the export name list head. */
56static PMYEXPORT g_pExpHead = NULL;
57/** Pointer to the next pointer for insertion. */
58static PMYEXPORT *g_ppExpNext = &g_pExpHead;
59
60
61
62static const char *leftStrip(const char *psz)
63{
64 while (isspace(*psz))
65 psz++;
66 return psz;
67}
68
69
70static char *leftStrip(char *psz)
71{
72 while (isspace(*psz))
73 psz++;
74 return psz;
75}
76
77
78static unsigned wordLength(const char *pszWord)
79{
80 unsigned off = 0;
81 char ch;
82 while ( (ch = pszWord[off]) != '\0'
83 && ch != '='
84 && ch != ','
85 && ch != ':'
86 && !isspace(ch) )
87 off++;
88 return off;
89}
90
91
92/**
93 * Parses the module definition file, collecting export information.
94 *
95 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
96 * details has been displayed.
97 * @param pInput The input stream.
98 */
99static RTEXITCODE parseInputInner(FILE *pInput)
100{
101 /*
102 * Process the file line-by-line.
103 */
104 bool fInExports = false;
105 unsigned iLine = 0;
106 char szLine[16384];
107 while (fgets(szLine, sizeof(szLine), pInput))
108 {
109 iLine++;
110
111 /*
112 * Strip leading and trailing spaces from the line as well as
113 * trailing comments.
114 */
115 char *psz = leftStrip(szLine);
116 if (*psz == ';')
117 continue; /* comment line. */
118
119 char *pszComment = strchr(psz, ';');
120 if (pszComment)
121 *pszComment = '\0';
122
123 unsigned cch = (unsigned)strlen(psz);
124 while (cch > 0 && (isspace(psz[cch - 1]) || psz[cch - 1] == '\r' || psz[cch - 1] == '\n'))
125 psz[--cch] = '\0';
126
127 if (!cch)
128 continue;
129
130 /*
131 * Check for known directives.
132 */
133 size_t cchWord0 = wordLength(psz);
134#define WORD_CMP(pszWord1, cchWord1, szWord2) \
135 ( (cchWord1) == sizeof(szWord2) - 1 && memcmp(pszWord1, szWord2, sizeof(szWord2) - 1) == 0 )
136 if (WORD_CMP(psz, cchWord0, "EXPORTS"))
137 {
138 fInExports = true;
139
140 /* In case there is an export on the same line. (Really allowed?) */
141 psz = leftStrip(psz + sizeof("EXPORTS") - 1);
142 if (!*psz)
143 continue;
144 }
145 /* Directives that we don't care about, but need to catch in order to
146 terminate the EXPORTS section in a timely manner. */
147 else if ( WORD_CMP(psz, cchWord0, "NAME")
148 || WORD_CMP(psz, cchWord0, "LIBRARY")
149 || WORD_CMP(psz, cchWord0, "DESCRIPTION")
150 || WORD_CMP(psz, cchWord0, "STACKSIZE")
151 || WORD_CMP(psz, cchWord0, "SECTIONS")
152 || WORD_CMP(psz, cchWord0, "SEGMENTS")
153 || WORD_CMP(psz, cchWord0, "VERSION")
154 )
155 {
156 fInExports = false;
157 }
158
159 /*
160 * Process exports:
161 * entryname[=internalname] [@ordinal[ ][NONAME]] [DATA] [PRIVATE]
162 */
163 if (fInExports)
164 {
165 const char *pchName = psz;
166 unsigned cchName = wordLength(psz);
167
168 psz = leftStrip(psz + cchName);
169 if (*psz == '=')
170 {
171 psz = leftStrip(psz + 1);
172 psz = leftStrip(psz + wordLength(psz));
173 }
174
175 bool fNoName = true;
176 unsigned uOrdinal = ~0U;
177 if (*psz == '@')
178 {
179 psz++;
180 if (!isdigit(*psz))
181 {
182 fprintf(stderr, "%s:%u: error: Invalid ordinal spec.\n", g_pszInput, iLine);
183 return RTEXITCODE_FAILURE;
184 }
185 uOrdinal = *psz++ - '0';
186 while (isdigit(*psz))
187 {
188 uOrdinal *= 10;
189 uOrdinal += *psz++ - '0';
190 }
191 psz = leftStrip(psz);
192 cch = wordLength(psz);
193 if (WORD_CMP(psz, cch, "NONAME"))
194 {
195#if 0
196 fNoName = true;
197 psz = leftStrip(psz + cch);
198#else
199 fprintf(stderr, "%s:%u: error: NONAME export not implemented.\n", g_pszInput, iLine);
200 return RTEXITCODE_FAILURE;
201#endif
202 }
203 }
204
205 while (*psz)
206 {
207 cch = wordLength(psz);
208 if (WORD_CMP(psz, cch, "DATA"))
209 {
210 if (!g_fIgnoreData)
211 {
212 fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n",
213 g_pszInput, iLine, cchName, pchName);
214 return RTEXITCODE_SUCCESS;
215 }
216 }
217 else if (!WORD_CMP(psz, cch, "PRIVATE"))
218 {
219 fprintf(stderr, "%s:%u: error: Cannot wrap up DATA export '%.*s'.\n",
220 g_pszInput, iLine, cchName, pchName);
221 return RTEXITCODE_SUCCESS;
222 }
223 psz = leftStrip(psz + cch);
224 }
225
226 /*
227 * Add the export.
228 */
229 PMYEXPORT pExp = (PMYEXPORT)malloc(sizeof(*pExp) + cchName);
230 if (!pExp)
231 {
232 fprintf(stderr, "%s:%u: error: Out of memory.\n", g_pszInput, iLine);
233 return RTEXITCODE_SUCCESS;
234 }
235 memcpy(pExp->szName, pchName, cchName);
236 pExp->szName[cchName] = '\0';
237 pExp->uOrdinal = uOrdinal;
238 pExp->fNoName = fNoName;
239 pExp->pNext = NULL;
240 *g_ppExpNext = pExp;
241 g_ppExpNext = &pExp->pNext;
242 }
243 }
244
245 /*
246 * Why did we quit the loop, EOF or error?
247 */
248 if (feof(pInput))
249 return RTEXITCODE_SUCCESS;
250 fprintf(stderr, "error: Read while reading '%s' (iLine=%u).\n", g_pszInput, iLine);
251 return RTEXITCODE_FAILURE;
252}
253
254
255/**
256 * Parses g_pszInput, populating the list pointed to by g_pExpHead.
257 *
258 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
259 * details has been displayed.
260 */
261static RTEXITCODE parseInput(void)
262{
263 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
264 FILE *pInput = fopen(g_pszInput, "r");
265 if (pInput)
266 {
267 rcExit = parseInputInner(pInput);
268 fclose(pInput);
269 if (rcExit == RTEXITCODE_SUCCESS && !g_pExpHead)
270 {
271 fprintf(stderr, "error: Found no exports in '%s'.\n", g_pszInput);
272 rcExit = RTEXITCODE_FAILURE;
273 }
274 }
275 else
276 fprintf(stderr, "error: Failed to open '%s' for reading.\n", g_pszInput);
277 return rcExit;
278}
279
280
281/**
282 * Generates the assembly source code, writing it to @a pOutput.
283 *
284 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
285 * details has been displayed.
286 * @param pOutput The output stream (caller checks it for errors
287 * when closing).
288 */
289static RTEXITCODE generateOutputInner(FILE *pOutput)
290{
291 fprintf(pOutput,
292 ";; Autogenerated from '%s'. DO NOT EDIT!\n"
293 "\n"
294 "%%include \"iprt/asmdefs.mac\"\n"
295 "\n"
296 "BEGINCODE\n",
297 g_pszInput);
298
299 for (PMYEXPORT pExp = g_pExpHead; pExp; pExp = pExp->pNext)
300 {
301 fprintf(pOutput,
302 "BEGINDATA\n"
303 "%%ifdef ASM_FORMAT_PE\n"
304 "global __imp_%s\n"
305 "__imp_%s:\n"
306 "%%endif\n"
307 "g_pfn%s RTCCPTR_DEF ___LazyLoad___%s\n"
308 "BEGINCODE\n"
309 "BEGINPROC %s\n"
310 " jmp RTCCPTR_PRE [g_pfn%s xWrtRIP]\n"
311 "ENDPROC %s\n"
312 "___LazyLoad___%s:\n"
313 /* "int3\n" */
314 "%%ifdef RT_ARCH_AMD64\n"
315 " lea rax, [.szName wrt rip]\n"
316 " lea r10, [g_pfn%s wrt rip]\n"
317 "%%elifdef RT_ARCH_X86\n"
318 " push .szName\n"
319 " push g_pfn%s\n"
320 "%%else\n"
321 " %%error \"Unsupported architecture\"\n"
322 "%%endif\n"
323 " call NAME(LazyLoadResolver)\n"
324 "%%ifdef RT_ARCH_X86\n"
325 " add esp, 8h\n"
326 "%%endif\n"
327 " jmp NAME(%s)\n"
328 ".szName db '%s',0\n"
329 "\n"
330 ,
331 pExp->szName,
332 pExp->szName,
333 pExp->szName, pExp->szName,
334 pExp->szName,
335 pExp->szName,
336 pExp->szName,
337 pExp->szName,
338 pExp->szName,
339 pExp->szName,
340 pExp->szName,
341 pExp->szName);
342 }
343
344 /*
345 * The code that does the loading and resolving.
346 */
347 fprintf(pOutput,
348 "BEGINDATA\n"
349 "g_hMod RTCCPTR_DEF 0\n"
350 "\n"
351 "BEGINCODE\n");
352
353 /*
354 * How we load the module needs to be selectable later on.
355 *
356 * The LazyLoading routine returns the module handle in RCX/ECX, caller
357 * saved all necessary registers.
358 */
359 fprintf(pOutput,
360 ";\n"
361 ";SUPR3DECL(int) SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod, \n"
362 "; uint32_t fFlags, PRTERRINFO pErrInfo);\n"
363 ";\n"
364 "extern IMPNAME(SUPR3HardenedLdrLoadAppPriv)\n"
365 "\n"
366 "BEGINPROC LazyLoading\n"
367 " mov xCX, [g_hMod xWrtRIP]\n"
368 " or xCX, xCX\n"
369 " jnz .return\n"
370 "\n"
371 "%%ifdef ASM_CALL64_GCC\n"
372 " xor rcx, rcx ; pErrInfo (local load)\n"
373 " xor rdx, rdx ; fFlags (local load)\n"
374 " lea rsi, [g_hMod wrt rip] ; phLdrMod\n"
375 " lea rdi, [.szLib wrt rip] ; pszFilename\n"
376 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
377 "\n"
378 "%%elifdef ASM_CALL64_MSC\n"
379 " xor r9, r9 ; pErrInfo (local load)\n"
380 " xor r8, r8 ; fFlags (local load)\n"
381 " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"
382 " lea rcx, [.szLib wrt rip] ; pszFilename\n"
383 " sub rsp, 20h\n"
384 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
385 " add rsp, 20h\n"
386 "\n"
387 "%%elifdef RT_ARCH_X86\n"
388 " push 0 ; pErrInfo\n"
389 " push 0 ; fFlags (local load)\n"
390 " push g_hMod ; phLdrMod\n"
391 " push .szLib ; pszFilename\n"
392 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
393 " add esp, 10h\n"
394 "%%else\n"
395 " %%error \"Unsupported architecture\"\n"
396 "%%endif\n"
397 " or eax, eax\n"
398 " jz .loadok\n"
399 ".badload:\n"
400 " int3\n"
401 " jmp .badload\n"
402 ".loadok:\n"
403 " mov xCX, [g_hMod xWrtRIP]\n"
404 ".return:\n"
405 " ret\n"
406 ".szLib db '%s',0\n"
407 "ENDPROC LazyLoading\n"
408 , g_pszLibrary);
409
410
411 fprintf(pOutput,
412 "\n"
413 ";\n"
414 ";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n"
415 ";\n"
416 "extern IMPNAME(RTLdrGetSymbol)\n"
417 "BEGINPROC LazyLoadResolver\n"
418 "%%ifdef RT_ARCH_AMD64\n"
419 " push rbp\n"
420 " mov rbp, rsp\n"
421 " push r15\n"
422 " push r14\n"
423 " mov r15, rax ; name\n"
424 " mov r14, r10 ; ppfn\n"
425 " push r9\n"
426 " push r8\n"
427 " push rcx\n"
428 " push rdx\n"
429 " %%ifdef ASM_CALL64_GCC\n"
430 " push rdi\n"
431 " push rsi\n"
432 " %%else\n"
433 " sub rsp, 20h\n"
434 " %%endif\n"
435 "\n"
436 " call NAME(LazyLoading) ; returns handle in rcx\n"
437 " %%ifdef ASM_CALL64_GCC\n"
438 " mov rdi, rcx ; hLdrMod\n"
439 " mov rsi, r15 ; pszSymbol\n"
440 " mov rdx, r14 ; ppvValue\n"
441 " %%else\n"
442 " mov rdx, r15 ; pszSymbol\n"
443 " mov r8, r14 ; ppvValue\n"
444 " %%endif\n"
445 " call IMP2(RTLdrGetSymbol)\n"
446 " or eax, eax\n"
447 ".badsym:\n"
448 " jz .symok\n"
449 " int3\n"
450 " jmp .badsym\n"
451 ".symok:\n"
452 "\n"
453 " %%ifdef ASM_CALL64_GCC\n"
454 " pop rdi\n"
455 " pop rsi\n"
456 " %%else\n"
457 " add rsp, 20h\n"
458 " %%endif\n"
459 " pop rdx\n"
460 " pop rcx\n"
461 " pop r8\n"
462 " pop r9\n"
463 " pop r14\n"
464 " pop r15\n"
465 " leave\n"
466 "\n"
467 "%%elifdef RT_ARCH_X86\n"
468 " push ebp\n"
469 " mov ebp, esp\n"
470 " push eax\n"
471 " push ecx\n"
472 " push edx\n"
473 "\n"
474 ".loaded:\n"
475 " mov eax, [ebp + 4] ; value addr\n"
476 " push eax\n"
477 " mov edx, [ebp + 8] ; symbol name\n"
478 " push edx\n"
479 " call NAME(LazyLoading) ; returns handle in ecx\n"
480 " mov ecx, [g_hMod]\n"
481 " call IMP2(RTLdrGetSymbol)\n"
482 " add esp, 12\n"
483 " or eax, eax\n"
484 ".badsym:\n"
485 " jz .symok\n"
486 " int3\n"
487 " jmp .badsym\n"
488 ".symok:\n"
489 " pop edx\n"
490 " pop ecx\n"
491 " pop eax\n"
492 " leave\n"
493 "%%else\n"
494 " %%error \"Unsupported architecture\"\n"
495 "%%endif\n"
496 " ret\n"
497 "ENDPROC LazyLoadResolver\n"
498 );
499
500 return RTEXITCODE_SUCCESS;
501}
502
503
504/**
505 * Generates the assembly source code, writing it to g_pszOutput.
506 *
507 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
508 * details has been displayed.
509 */
510static RTEXITCODE generateOutput(void)
511{
512 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
513 FILE *pOutput = fopen(g_pszOutput, "w");
514 if (pOutput)
515 {
516 rcExit = generateOutputInner(pOutput);
517 if (fclose(pOutput))
518 {
519 fprintf(stderr, "error: Error closing '%s'.\n", g_pszOutput);
520 rcExit = RTEXITCODE_FAILURE;
521 }
522 }
523 else
524 fprintf(stderr, "error: Failed to open '%s' for writing.\n", g_pszOutput);
525 return rcExit;
526}
527
528
529/**
530 * Displays usage information.
531 *
532 * @returns RTEXITCODE_SUCCESS.
533 * @param pszArgv0 The argv[0] string.
534 */
535static int usage(const char *pszArgv0)
536{
537 printf("usage: %s --libary <loadname> --output <lazyload.asm> <input.def>\n",
538 "\n"
539 "Copyright (C) 2013 Oracle Corporation\n"
540 , pszArgv0);
541
542 return RTEXITCODE_SUCCESS;
543}
544
545
546int main(int argc, char **argv)
547{
548 /*
549 * Parse options.
550 */
551 for (int i = 1; i < argc; i++)
552 {
553 const char *psz = argv[i];
554 if (*psz == '-')
555 {
556 if (!strcmp(psz, "--output") || !strcmp(psz, "-o"))
557 {
558 if (++i >= argc)
559 {
560 fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz);
561 return RTEXITCODE_SYNTAX;
562 }
563 g_pszOutput = argv[i];
564 }
565 else if (!strcmp(psz, "--library") || !strcmp(psz, "-l"))
566 {
567 if (++i >= argc)
568 {
569 fprintf(stderr, "syntax error: Library name expected after '%s'.\n", psz);
570 return RTEXITCODE_SYNTAX;
571 }
572 g_pszLibrary = argv[i];
573 }
574 /** @todo Support different load methods so this can be used on system libs and
575 * such if we like. */
576 else if ( !strcmp(psz, "--help")
577 || !strcmp(psz, "-help")
578 || !strcmp(psz, "-h")
579 || !strcmp(psz, "-?") )
580 return usage(argv[0]);
581 else if ( !strcmp(psz, "--version")
582 || !strcmp(psz, "-V"))
583 {
584 printf("$Revision: 46475 $\n");
585 return RTEXITCODE_SUCCESS;
586 }
587 else
588 {
589 fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz);
590 return RTEXITCODE_SYNTAX;
591 }
592 }
593 else
594 {
595 if (g_pszInput)
596 {
597 fprintf(stderr, "syntax error: Already specified '%s' as the input file.\n", g_pszInput);
598 return RTEXITCODE_SYNTAX;
599 }
600 g_pszInput = argv[i];
601 }
602 }
603 if (!g_pszInput)
604 {
605 fprintf(stderr, "syntax error: No input file specified.\n");
606 return RTEXITCODE_SYNTAX;
607 }
608 if (!g_pszOutput)
609 {
610 fprintf(stderr, "syntax error: No output file specified.\n");
611 return RTEXITCODE_SYNTAX;
612 }
613 if (!g_pszLibrary)
614 {
615 fprintf(stderr, "syntax error: No library name specified.\n");
616 return RTEXITCODE_SYNTAX;
617 }
618
619 /*
620 * Do the job.
621 */
622 RTEXITCODE rcExit = parseInput();
623 if (rcExit == RTEXITCODE_SUCCESS)
624 rcExit = generateOutput();
625 return rcExit;
626}
627
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette