VirtualBox

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

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

VBoxDef2LazyLoad.cpp: Attempt at correct stack alignment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Id: VBoxDef2LazyLoad.cpp 47685 2013-08-13 10:19:25Z 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 " sub rsp, 08h\n"
377 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
378 " add rsp, 08h\n"
379 "\n"
380 "%%elifdef ASM_CALL64_MSC\n"
381 " xor r9, r9 ; pErrInfo (local load)\n"
382 " xor r8, r8 ; fFlags (local load)\n"
383 " lea rdx, [g_hMod wrt rip] ; phLdrMod\n"
384 " lea rcx, [.szLib wrt rip] ; pszFilename\n"
385 " sub rsp, 28h\n"
386 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
387 " add rsp, 28h\n"
388 "\n"
389 "%%elifdef RT_ARCH_X86\n"
390 " sub rsp, 0ch\n"
391 " push 0 ; pErrInfo\n"
392 " push 0 ; fFlags (local load)\n"
393 " push g_hMod ; phLdrMod\n"
394 " push .szLib ; pszFilename\n"
395 " call IMP2(SUPR3HardenedLdrLoadAppPriv)\n"
396 " add esp, 1ch\n"
397 "%%else\n"
398 " %%error \"Unsupported architecture\"\n"
399 "%%endif\n"
400 " or eax, eax\n"
401 " jz .loadok\n"
402 ".badload:\n"
403 " int3\n"
404 " jmp .badload\n"
405 ".loadok:\n"
406 " mov xCX, [g_hMod xWrtRIP]\n"
407 ".return:\n"
408 " ret\n"
409 ".szLib db '%s',0\n"
410 "ENDPROC LazyLoading\n"
411 , g_pszLibrary);
412
413
414 fprintf(pOutput,
415 "\n"
416 ";\n"
417 ";RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue);\n"
418 ";\n"
419 "extern IMPNAME(RTLdrGetSymbol)\n"
420 "BEGINPROC LazyLoadResolver\n"
421 "%%ifdef RT_ARCH_AMD64\n"
422 " push rbp\n"
423 " mov rbp, rsp\n"
424 " push r15\n"
425 " push r14\n"
426 " mov r15, rax ; name\n"
427 " mov r14, r10 ; ppfn\n"
428 " push r9\n"
429 " push r8\n"
430 " push rcx\n"
431 " push rdx\n"
432 " push r12\n"
433 " %%ifdef ASM_CALL64_GCC\n"
434 " push rsi\n"
435 " push rdi\n"
436 " mov r12, rsp\n"
437 " %%else\n"
438 " mov r12, rsp\n"
439 " sub rsp, 20h\n"
440 " %%endif\n"
441 " and rsp, 0fffffff0h ; Try make sure the stack is aligned\n"
442 "\n"
443 " call NAME(LazyLoading) ; returns handle in rcx\n"
444 " %%ifdef ASM_CALL64_GCC\n"
445 " mov rdi, rcx ; hLdrMod\n"
446 " mov rsi, r15 ; pszSymbol\n"
447 " mov rdx, r14 ; ppvValue\n"
448 " %%else\n"
449 " mov rdx, r15 ; pszSymbol\n"
450 " mov r8, r14 ; ppvValue\n"
451 " %%endif\n"
452 " call IMP2(RTLdrGetSymbol)\n"
453 " or eax, eax\n"
454 ".badsym:\n"
455 " jz .symok\n"
456 " int3\n"
457 " jmp .badsym\n"
458 ".symok:\n"
459 "\n"
460 " mov rsp, r12\n"
461 " %%ifdef ASM_CALL64_GCC\n"
462 " pop rdi\n"
463 " pop rsi\n"
464 " %%endif\n"
465 " pop r12\n"
466 " pop rdx\n"
467 " pop rcx\n"
468 " pop r8\n"
469 " pop r9\n"
470 " pop r14\n"
471 " pop r15\n"
472 " leave\n"
473 "\n"
474 "%%elifdef RT_ARCH_X86\n"
475 " push ebp\n"
476 " mov ebp, esp\n"
477 " push eax\n"
478 " push ecx\n"
479 " push edx\n"
480 " and esp, 0fffffff0h\n"
481 "\n"
482 ".loaded:\n"
483 " mov eax, [ebp + 4] ; value addr\n"
484 " push eax\n"
485 " mov edx, [ebp + 8] ; symbol name\n"
486 " push edx\n"
487 " call NAME(LazyLoading) ; returns handle in ecx\n"
488 " mov ecx, [g_hMod]\n"
489 " call IMP2(RTLdrGetSymbol)\n"
490 " or eax, eax\n"
491 ".badsym:\n"
492 " jz .symok\n"
493 " int3\n"
494 " jmp .badsym\n"
495 ".symok:\n"
496 " lea esp, [ebp - 0ch]\n"
497 " pop edx\n"
498 " pop ecx\n"
499 " pop eax\n"
500 " leave\n"
501 "%%else\n"
502 " %%error \"Unsupported architecture\"\n"
503 "%%endif\n"
504 " ret\n"
505 "ENDPROC LazyLoadResolver\n"
506 );
507
508 return RTEXITCODE_SUCCESS;
509}
510
511
512/**
513 * Generates the assembly source code, writing it to g_pszOutput.
514 *
515 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE, in the latter case full
516 * details has been displayed.
517 */
518static RTEXITCODE generateOutput(void)
519{
520 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
521 FILE *pOutput = fopen(g_pszOutput, "w");
522 if (pOutput)
523 {
524 rcExit = generateOutputInner(pOutput);
525 if (fclose(pOutput))
526 {
527 fprintf(stderr, "error: Error closing '%s'.\n", g_pszOutput);
528 rcExit = RTEXITCODE_FAILURE;
529 }
530 }
531 else
532 fprintf(stderr, "error: Failed to open '%s' for writing.\n", g_pszOutput);
533 return rcExit;
534}
535
536
537/**
538 * Displays usage information.
539 *
540 * @returns RTEXITCODE_SUCCESS.
541 * @param pszArgv0 The argv[0] string.
542 */
543static int usage(const char *pszArgv0)
544{
545 printf("usage: %s --libary <loadname> --output <lazyload.asm> <input.def>\n"
546 "\n"
547 "Copyright (C) 2013 Oracle Corporation\n"
548 , pszArgv0);
549
550 return RTEXITCODE_SUCCESS;
551}
552
553
554int main(int argc, char **argv)
555{
556 /*
557 * Parse options.
558 */
559 for (int i = 1; i < argc; i++)
560 {
561 const char *psz = argv[i];
562 if (*psz == '-')
563 {
564 if (!strcmp(psz, "--output") || !strcmp(psz, "-o"))
565 {
566 if (++i >= argc)
567 {
568 fprintf(stderr, "syntax error: File name expected after '%s'.\n", psz);
569 return RTEXITCODE_SYNTAX;
570 }
571 g_pszOutput = argv[i];
572 }
573 else if (!strcmp(psz, "--library") || !strcmp(psz, "-l"))
574 {
575 if (++i >= argc)
576 {
577 fprintf(stderr, "syntax error: Library name expected after '%s'.\n", psz);
578 return RTEXITCODE_SYNTAX;
579 }
580 g_pszLibrary = argv[i];
581 }
582 /** @todo Support different load methods so this can be used on system libs and
583 * such if we like. */
584 else if ( !strcmp(psz, "--help")
585 || !strcmp(psz, "-help")
586 || !strcmp(psz, "-h")
587 || !strcmp(psz, "-?") )
588 return usage(argv[0]);
589 else if ( !strcmp(psz, "--version")
590 || !strcmp(psz, "-V"))
591 {
592 printf("$Revision: 47685 $\n");
593 return RTEXITCODE_SUCCESS;
594 }
595 else
596 {
597 fprintf(stderr, "syntax error: Unknown option '%s'.\n", psz);
598 return RTEXITCODE_SYNTAX;
599 }
600 }
601 else
602 {
603 if (g_pszInput)
604 {
605 fprintf(stderr, "syntax error: Already specified '%s' as the input file.\n", g_pszInput);
606 return RTEXITCODE_SYNTAX;
607 }
608 g_pszInput = argv[i];
609 }
610 }
611 if (!g_pszInput)
612 {
613 fprintf(stderr, "syntax error: No input file specified.\n");
614 return RTEXITCODE_SYNTAX;
615 }
616 if (!g_pszOutput)
617 {
618 fprintf(stderr, "syntax error: No output file specified.\n");
619 return RTEXITCODE_SYNTAX;
620 }
621 if (!g_pszLibrary)
622 {
623 fprintf(stderr, "syntax error: No library name specified.\n");
624 return RTEXITCODE_SYNTAX;
625 }
626
627 /*
628 * Do the job.
629 */
630 RTEXITCODE rcExit = parseInput();
631 if (rcExit == RTEXITCODE_SUCCESS)
632 rcExit = generateOutput();
633 return rcExit;
634}
635
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use