VirtualBox

source: kStuff/trunk/kRdr/kRdrFile.cpp

Last change on this file was 81, checked in by bird, 8 years ago

kLdr: some refactoring and fixes.

  • Property svn:keywords set to Id Revision
File size: 36.9 KB
Line 
1/* $Id: kRdrFile.cpp 81 2016-08-18 22:10:38Z bird $ */
2/** @file
3 * kRdrFile - The Native File Provider
4 */
5
6/*
7 * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include "kRdrInternal.h"
35#include <k/kHlpAlloc.h>
36#include <k/kHlpString.h>
37#include <k/kErrors.h>
38
39#if K_OS == K_OS_DARWIN \
40 || K_OS == K_OS_FREEBSD \
41 || K_OS == K_OS_LINUX \
42 || K_OS == K_OS_NETBSD \
43 || K_OS == K_OS_OPENBSD \
44 || K_OS == K_OS_SOLARIS
45# include <k/kHlpSys.h>
46# include <sys/fcntl.h>
47# include <sys/mman.h>
48# include <unistd.h>
49
50#elif K_OS == K_OS_OS2
51# define INCL_ERRORS
52# define INCL_BASE
53# include <os2.h>
54
55#elif K_OS == K_OS_WINDOWS
56# define WIN32_NO_STATUS
57# include <Windows.h>
58# include <ntsecapi.h>
59# include <ntstatus.h>
60
61# ifdef __cplusplus
62 extern "C" {
63# endif
64
65 /** @todo find a non-conflicting header with NTSTATUS, NTAPI, ++ */
66 typedef LONG NTSTATUS;
67 #define NT_SUCCESS(x) ((x)>=0)
68
69 typedef struct _OBJECT_ATTRIBUTES
70 {
71 ULONG Length;
72 HANDLE RootDirectory;
73 PUNICODE_STRING ObjectName;
74 ULONG Attributes;
75 PVOID SecurityDescriptor;
76 PVOID SecurityQualityOfService;
77 } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
78
79 typedef enum _SECTION_INHERIT
80 {
81 ViewShare = 1,
82 ViewUnmap = 2
83 } SECTION_INHERIT;
84
85# define NTOSAPI __declspec(dllimport)
86# define NtCurrentProcess() GetCurrentProcess()
87
88# ifndef MEM_DOS_LIM
89# define MEM_DOS_LIM 0x40000000UL
90# endif
91
92 NTOSAPI
93 NTSTATUS
94 NTAPI
95 NtCreateSection(
96 OUT PHANDLE SectionHandle,
97 IN ACCESS_MASK DesiredAccess,
98 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
99 IN PLARGE_INTEGER SectionSize OPTIONAL,
100 IN ULONG Protect,
101 IN ULONG Attributes,
102 IN HANDLE FileHandle OPTIONAL
103 );
104
105 NTOSAPI
106 NTSTATUS
107 NTAPI
108 NtMapViewOfSection(
109 IN HANDLE SectionHandle,
110 IN HANDLE ProcessHandle,
111 IN OUT PVOID *BaseAddress,
112 IN ULONG ZeroBits,
113 IN ULONG CommitSize,
114 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
115 IN OUT PSIZE_T ViewSize,
116 IN SECTION_INHERIT InheritDisposition,
117 IN ULONG AllocationType,
118 IN ULONG Protect
119 );
120
121 NTOSAPI
122 NTSTATUS
123 NTAPI
124 NtUnmapViewOfSection(
125 IN HANDLE ProcessHandle,
126 IN PVOID BaseAddress
127 );
128
129 NTOSAPI
130 NTSTATUS
131 NTAPI
132 NtClose(
133 IN HANDLE Handle
134 );
135
136 NTOSAPI
137 NTSTATUS
138 NTAPI
139 ZwProtectVirtualMemory(
140 IN HANDLE ProcessHandle,
141 IN OUT PVOID *BaseAddress,
142 IN OUT PSIZE_T ProtectSize,
143 IN ULONG NewProtect,
144 OUT PULONG OldProtect
145 );
146# define NtProtectVirtualMemory ZwProtectVirtualMemory
147
148 NTOSAPI
149 NTSTATUS
150 NTAPI
151 NtAllocateVirtualMemory(
152 IN HANDLE ProcessHandle,
153 IN OUT PVOID *BaseAddress,
154 IN ULONG ZeroBits,
155 IN OUT PSIZE_T AllocationSize,
156 IN ULONG AllocationType,
157 IN ULONG Protect
158 );
159
160 NTOSAPI
161 NTSTATUS
162 NTAPI
163 NtFreeVirtualMemory(
164 IN HANDLE ProcessHandle,
165 IN OUT PVOID *BaseAddress,
166 IN OUT PSIZE_T FreeSize,
167 IN ULONG FreeType
168 );
169
170# ifdef __cplusplus
171 }
172# endif
173
174#else
175# error "port me"
176#endif
177
178
179/*******************************************************************************
180* Structures and Typedefs *
181*******************************************************************************/
182/**
183 * Prepared stuff.
184 */
185typedef struct KRDRFILEPREP
186{
187 /** The address of the prepared region. */
188 void *pv;
189 /** The size of the prepared region. */
190 KSIZE cb;
191#if K_OS == K_OS_WINDOWS
192 /** Handle to the section created to map the file. */
193 HANDLE hSection;
194#endif
195} KRDRFILEPREP, *PKRDRFILEPREP;
196
197/**
198 * The file provier instance for native files.
199 */
200typedef struct KRDRFILE
201{
202 /** The file reader vtable. */
203 KRDR Core;
204 /** The file handle. */
205#if K_OS == K_OS_DARWIN \
206 || K_OS == K_OS_LINUX \
207 || K_OS == K_OS_NETBSD \
208 || K_OS == K_OS_OPENBSD \
209 || K_OS == K_OS_SOLARIS
210 int File;
211#elif K_OS == K_OS_OS2
212 HFILE File;
213#elif K_OS == K_OS_WINDOWS
214 HANDLE File;
215#else
216# error "Port me!"
217#endif
218 /** The current file offset. */
219 KFOFF off;
220 /** The file size. */
221 KFOFF cb;
222 /** Array where we stuff the mapping area data. */
223 KRDRFILEPREP aPreps[4];
224 /** The number of current preps. */
225 KU32 cPreps;
226 /** Number of mapping references. */
227 KI32 cMappings;
228 /** The memory mapping. */
229 void *pvMapping;
230 /** The filename. */
231 char szFilename[1];
232} KRDRFILE, *PKRDRFILE;
233
234
235/*******************************************************************************
236* Internal Functions *
237*******************************************************************************/
238static void krdrFileDone(PKRDR pRdr);
239static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
240static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
241static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
242static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
243static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
244static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
245static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
246static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
247static KSIZE krdrFilePageSize(PKRDR pRdr);
248static const char *krdrFileName(PKRDR pRdr);
249static KIPTR krdrFileNativeFH(PKRDR pRdr);
250static KFOFF krdrFileTell(PKRDR pRdr);
251static KFOFF krdrFileSize(PKRDR pRdr);
252static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits);
253static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits);
254static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
255static int krdrFileDestroy(PKRDR pRdr);
256static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename);
257
258
259/*******************************************************************************
260* Global Variables *
261*******************************************************************************/
262/** Native file provider operations. */
263const KRDROPS g_kRdrFileOps =
264{
265 "native file",
266 NULL,
267 krdrFileCreate,
268 krdrFileDestroy,
269 krdrFileRead,
270 krdrFileAllMap,
271 krdrFileAllUnmap,
272 krdrFileSize,
273 krdrFileTell,
274 krdrFileName,
275 krdrFileNativeFH,
276 krdrFilePageSize,
277 krdrFileMap,
278 krdrFileRefresh,
279 krdrFileProtect,
280 krdrFileUnmap,
281 krdrFileDone,
282 42
283};
284
285
286#if K_OS == K_OS_WINDOWS
287/**
288 * Converts a kLdr segment protection to NT protection for a mapping.
289 *
290 * @returns Nt page protection.
291 * @param enmProt kLdr protection.
292 */
293static ULONG krdrFileGetNtMapProt(KPROT enmProt)
294{
295 switch (enmProt)
296 {
297 case KPROT_NOACCESS: return PAGE_NOACCESS;
298 case KPROT_READONLY: return PAGE_READONLY;
299 case KPROT_READWRITE: return PAGE_READWRITE;
300 case KPROT_WRITECOPY: return PAGE_WRITECOPY;
301 case KPROT_EXECUTE: return PAGE_EXECUTE;
302 case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
303 case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
304 case KPROT_EXECUTE_WRITECOPY: return PAGE_EXECUTE_WRITECOPY;
305 default: return ~(ULONG)0;
306 }
307}
308
309
310/**
311 * Converts a kLdr segment protection to NT protection for a allocation.
312 *
313 * @returns Nt page protection.
314 * @param enmProt kLdr protection.
315 */
316static ULONG krdrFileGetNtAllocProt(KPROT enmProt)
317{
318 switch (enmProt)
319 {
320 case KPROT_NOACCESS: return PAGE_NOACCESS;
321 case KPROT_READONLY: return PAGE_READONLY;
322 case KPROT_WRITECOPY:
323 case KPROT_READWRITE: return PAGE_READWRITE;
324 case KPROT_EXECUTE: return PAGE_EXECUTE;
325 case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
326 case KPROT_EXECUTE_WRITECOPY:
327 case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
328 default: return ~(ULONG)0;
329 }
330}
331#endif
332
333
334/** @copydoc KRDROPS::pfnDone */
335static void krdrFileDone(PKRDR pRdr)
336{
337}
338
339
340/**
341 * Finds a prepared mapping region.
342 *
343 * @returns Pointer to the aPrep entry.
344 * @param pFile The instance data.
345 * @param pv The base of the region.
346 */
347static PKRDRFILEPREP krdrFileFindPrepExact(PKRDRFILE pFile, void *pv)
348{
349 KI32 i = pFile->cPreps;
350 while (i-- > 0)
351 if (pFile->aPreps[i].pv == pv)
352 return &pFile->aPreps[i];
353 return NULL;
354}
355
356
357/** @copydoc KRDROPS::pfnUnmap */
358static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
359{
360 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
361 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
362 int rc;
363 if (!pPrep)
364 return KERR_INVALID_PARAMETER;
365
366#if K_OS == K_OS_WINDOWS
367 if (pPrep->hSection != NULL)
368 {
369 /** @todo implement me. */
370 return -1;
371 }
372#endif
373
374 rc = krdrFileGenericUnmap(pRdr, pPrep, cSegments, paSegments);
375
376 /* remove the mapping data on success. */
377 if (!rc)
378 {
379 pRdrFile->cPreps--;
380 if (pPrep != &pRdrFile->aPreps[pRdrFile->cPreps])
381 *pPrep = pRdrFile->aPreps[pRdrFile->cPreps];
382 }
383 return rc;
384}
385
386
387/** Generic implementation of krdrFileUnmap. */
388static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
389{
390 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
391 return kHlpPageFree(pPrep->pv, pPrep->cb);
392}
393
394
395/** @copydoc KRDROPS::pfnProtect */
396static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
397{
398 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
399 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
400 if (!pPrep)
401 return KERR_INVALID_PARAMETER;
402
403#if K_OS == K_OS_WINDOWS
404 if (pPrep->hSection != NULL)
405 {
406 /** @todo implement me. */
407 return -1;
408 }
409#endif
410
411 return krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect);
412}
413
414
415/** Generic implementation of krdrFileProtect. */
416static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
417{
418 KU32 i;
419
420 /*
421 * Iterate the segments and apply memory protection changes.
422 */
423 for (i = 0; i < cSegments; i++)
424 {
425 int rc;
426 void *pv;
427 KPROT enmProt;
428
429 if (paSegments[i].RVA == NIL_KLDRADDR)
430 continue;
431
432 /* calc new protection. */
433 enmProt = (KPROT)paSegments[i].enmProt; /** @todo drop cast */
434 if (fUnprotectOrProtect)
435 {
436 switch (enmProt)
437 {
438 case KPROT_NOACCESS:
439 case KPROT_READONLY:
440 case KPROT_READWRITE:
441 case KPROT_WRITECOPY:
442 enmProt = KPROT_READWRITE;
443 break;
444 case KPROT_EXECUTE:
445 case KPROT_EXECUTE_READ:
446 case KPROT_EXECUTE_READWRITE:
447 case KPROT_EXECUTE_WRITECOPY:
448 enmProt = KPROT_EXECUTE_READWRITE;
449 break;
450 default:
451 kRdrAssert(!"bad enmProt");
452 return -1;
453 }
454 }
455 else
456 {
457 /* copy on write -> normal write. */
458 if (enmProt == KPROT_EXECUTE_WRITECOPY)
459 enmProt = KPROT_EXECUTE_READWRITE;
460 else if (enmProt == KPROT_WRITECOPY)
461 enmProt = KPROT_READWRITE;
462 }
463
464 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
465
466 rc = kHlpPageProtect(pv, paSegments[i].cbMapped, enmProt);
467 if (rc)
468 break;
469 }
470
471 return 0;
472}
473
474
475/** @copydoc KRDROPS::pfnRefresh */
476static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
477{
478 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
479 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
480 if (!pPrep)
481 return KERR_INVALID_PARAMETER;
482
483#if K_OS == K_OS_WINDOWS
484 if (pPrep->hSection != NULL)
485 {
486 /** @todo implement me. */
487 return -1;
488 }
489#endif
490
491 return krdrFileGenericRefresh(pRdr, pPrep, cSegments, paSegments);
492}
493
494
495/** Generic implementation of krdrFileRefresh. */
496static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
497{
498 int rc;
499 int rc2;
500 KU32 i;
501
502 /*
503 * Make everything writable again.
504 */
505 rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
506 if (rc)
507 {
508 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
509 return rc;
510 }
511
512 /*
513 * Clear everything.
514 */
515 /** @todo only zero the areas not covered by raw file bits. */
516 kHlpMemSet(pPrep->pv, 0, pPrep->cb);
517
518 /*
519 * Reload all the segments.
520 * We could possibly skip some segments, but we currently have
521 * no generic way of figuring out which at the moment.
522 */
523 for (i = 0; i < cSegments; i++)
524 {
525 void *pv;
526
527 if ( paSegments[i].RVA == NIL_KLDRADDR
528 || paSegments[i].cbFile <= 0)
529 continue;
530
531 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
532 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
533 if (rc)
534 break;
535 }
536
537 /*
538 * Protect the bits again.
539 */
540 rc2 = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
541 if (rc2 && rc)
542 rc = rc2;
543
544 return rc;
545}
546
547
548/** @copydoc KRDROPS::pfnMap */
549static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
550{
551 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
552 PKRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps];
553 KLDRSIZE cbTotal;
554 const KSIZE cbPage = pRdr->pOps->pfnPageSize(pRdr);
555 int rc;
556 KU32 i;
557
558 if (pRdrFile->cPreps >= K_ELEMENTS(pRdrFile->aPreps))
559 return KRDR_ERR_TOO_MANY_MAPPINGS;
560
561 /*
562 * Calc the total mapping space needed.
563 */
564 cbTotal = 0;
565 for (i = 0; i < cSegments; i++)
566 {
567 KLDRSIZE uRVASegmentEnd;
568 if (paSegments[i].RVA == NIL_KLDRADDR)
569 continue;
570 uRVASegmentEnd = paSegments[i].RVA + paSegments[i].cbMapped;
571 if (cbTotal < uRVASegmentEnd)
572 cbTotal = uRVASegmentEnd;
573 }
574 pPrep->cb = (KSIZE)cbTotal;
575 if (pPrep->cb != cbTotal)
576 return KLDR_ERR_ADDRESS_OVERFLOW;
577 pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1);
578
579#if K_OS == K_OS_DARWIN \
580 || K_OS == K_OS_FREEBSD \
581 || K_OS == K_OS_LINUX \
582 || K_OS == K_OS_NETBSD \
583 || K_OS == K_OS_OPENBSD \
584 || K_OS == K_OS_SOLARIS
585 /** @todo */
586
587#elif K_OS == K_OS_WINDOWS
588 /*
589 * The NT memory mapped file API sucks in a lot of ways. Unless you're actually
590 * trying to map a PE image and the kernel can parse the file for it self, the
591 * API just isn't up to scratch.
592 *
593 * Problems:
594 * 1. Reserving memory for the views is risky because you can't reserve and
595 * map into the reserved space. So, other threads might grab the memory
596 * before we get to it.
597 * 2. The page aligning of file offsets makes it impossible to map most
598 * executable images since these are commonly sector aligned.
599 * 3. When mapping a read+execute file, its not possible to create section
600 * larger than the file since the section size is bound to the data file
601 * size. This wouldn't have been such a problem if it was possible to
602 * map views beyond the section restriction, i.e. have a file size and
603 * view size.
604 * 4. Only x86 can map views at page granularity it seems, and that only
605 * using an undocument flag. The default granularity is 64KB.
606 * 5. There is more crappyness here...
607 *
608 * So, first we'll have to check if we can the file using the crappy NT APIs.
609 * Chances are we can't.
610 */
611 for (i = 0; i < cSegments; i++)
612 {
613 if (paSegments[i].RVA == NIL_KLDRADDR)
614 continue;
615
616 /* The file backing of the segments must be page aligned. */
617 if ( paSegments[i].cbFile > 0
618 && paSegments[i].offFile & (cbPage - 1))
619 break;
620
621 /* Only page alignment gaps between the file size and the mapping size. */
622 if ( paSegments[i].cbFile > 0
623 && (paSegments[i].cbFile & ~(cbPage - 1)) != (paSegments[i].cbMapped & ~(cbPage - 1)) )
624 break;
625
626 /* The mapping addresses of the segments must be page aligned.
627 * Non-x86 will probably require 64KB alignment here. */
628 if (paSegments[i].RVA & (cbPage - 1))
629 break;
630
631 /* If we do have to allocate the segment it's RVA must be 64KB aligned. */
632 if ( paSegments[i].cbFile > 0
633 && (paSegments[i].RVA & 0xffff))
634 break;
635 }
636 /** @todo if this is a PE image, we might just try a SEC_IMAGE mapping. It'll work if the host and image machines matches. */
637 if (i == cSegments)
638 {
639 /* WOW! it may work out! Incredible! */
640 SIZE_T ViewSize;
641 LARGE_INTEGER SectionOffset;
642 LARGE_INTEGER MaxiumSize;
643 NTSTATUS Status;
644 PVOID pv;
645
646 MaxiumSize.QuadPart = pRdr->pOps->pfnSize(pRdr);
647 if (MaxiumSize.QuadPart > (LONGLONG)cbTotal)
648 MaxiumSize.QuadPart = cbTotal;
649
650 Status = NtCreateSection(&pPrep->hSection,
651 SECTION_MAP_EXECUTE | SECTION_MAP_READ, /* desired access */
652 NULL, /* object attributes */
653 &MaxiumSize,
654 PAGE_EXECUTE_WRITECOPY, /* page attributes */
655 SEC_COMMIT, /* section attributes */
656 pRdrFile->File);
657 if (!NT_SUCCESS(Status))
658 return (int)Status;
659
660 /*
661 * Determin the base address.
662 */
663 if (fFixed)
664 pPrep->pv = *ppvBase;
665 else
666 {
667 pv = NULL;
668 ViewSize = (KSIZE)cbTotal;
669
670 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
671 &pv,
672 0, /* ZeroBits */
673 &ViewSize,
674 MEM_RESERVE,
675 PAGE_READONLY);
676 if (NT_SUCCESS(Status))
677 {
678 pPrep->pv = *ppvBase = pv;
679 ViewSize = 0;
680 Status = NtFreeVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, MEM_RELEASE);
681 }
682 if (!NT_SUCCESS(Status))
683 {
684 NtClose(pPrep->hSection);
685 return Status;
686 }
687 }
688
689 /*
690 * Map the segments.
691 */
692 for (i = 0; i < cSegments; i++)
693 {
694 ULONG fPageProt;
695
696 if (paSegments[i].RVA == NIL_KLDRADDR)
697 continue;
698
699 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
700 if (paSegments[i].cbFile > 0)
701 {
702 SectionOffset.QuadPart = paSegments[i].offFile;
703 ViewSize = paSegments[i].cbFile;
704 fPageProt = krdrFileGetNtMapProt(paSegments[i].enmProt);
705 /* STATUS_MAPPED_ALIGNMENT
706 STATUS_CONFLICTING_ADDRESSES
707 STATUS_INVALID_VIEW_SIZE */
708 Status = NtMapViewOfSection(pPrep->hSection, NtCurrentProcess(),
709 &pv,
710 0, /* ZeroBits */
711 0, /* CommitSize */
712 &SectionOffset, /* SectionOffset */
713 &ViewSize,
714 ViewUnmap,
715 MEM_DOS_LIM, /* AllocationType */
716 fPageProt);
717 /* do we have to zero anything? */
718 if ( NT_SUCCESS(Status)
719 && 0/*later*/)
720 {
721 /*ULONG OldPageProt = 0;
722 NtProtectVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, , */
723 }
724 }
725 else
726 {
727 ViewSize = paSegments[i].cbMapped;
728 fPageProt = krdrFileGetNtAllocProt(paSegments[i].enmProt);
729 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
730 &pv,
731 0, /* ZeroBits */
732 &ViewSize,
733 MEM_COMMIT,
734 fPageProt);
735 }
736 if (!NT_SUCCESS(Status))
737 break;
738 }
739
740 /*
741 * On success, commit the mapping and return.
742 */
743 if (NT_SUCCESS(Status))
744 {
745 pRdrFile->cPreps++;
746 return 0;
747 }
748
749 /* bail out and fall back on the generic code. */
750 while (i-- > 0)
751 {
752 PVOID pv;
753
754 if (paSegments[i].RVA == NIL_KLDRADDR)
755 continue;
756
757 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
758 if (paSegments[i].cbFile > 0)
759 NtUnmapViewOfSection(NtCurrentProcess(), pv);
760 else
761 NtFreeVirtualMemory(NtCurrentProcess(), &pv, NULL, MEM_RELEASE);
762 }
763 NtClose(pPrep->hSection);
764 }
765 /* else: fall back to the generic code */
766 pPrep->hSection = NULL;
767#endif
768
769 /*
770 * Use the generic map emulation.
771 */
772 pPrep->pv = fFixed ? *ppvBase : NULL;
773 rc = krdrFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed);
774 if (!rc)
775 {
776 *ppvBase = pPrep->pv;
777 pRdrFile->cPreps++;
778 }
779
780 return rc;
781}
782
783
784/** Generic implementation of krdrFileMap. */
785static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
786{
787 int rc;
788 KU32 i;
789
790 /*
791 * Generic mapping code using kHlpPageAlloc(), kHlpPageFree() and kHlpPageProtect().
792 */
793 rc = kHlpPageAlloc(&pPrep->pv, pPrep->cb, KPROT_EXECUTE_READWRITE, fFixed);
794 if (rc)
795 return rc;
796
797 /*
798 * Load the data.
799 */
800 for (i = 0; i < cSegments; i++)
801 {
802 void *pv;
803
804 if ( paSegments[i].RVA == NIL_KLDRADDR
805 || paSegments[i].cbFile <= 0)
806 continue;
807
808 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
809 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
810 if (rc)
811 break;
812 }
813
814 /*
815 * Set segment protection.
816 */
817 if (!rc)
818 {
819 rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
820 if (!rc)
821 return 0;
822 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
823 }
824
825 /* bailout */
826 kHlpPageFree(pPrep->pv, pPrep->cb);
827 return rc;
828}
829
830
831/** @copydoc KRDROPS::pfnPageSize */
832static KSIZE krdrFilePageSize(PKRDR pRdr)
833{
834#if K_OS == K_OS_DARWIN
835 return 0x1000; /** @todo find some header somewhere... */
836
837#elif K_OS == K_OS_LINUX
838 return 0x1000; /** @todo find some header somewhere... */
839
840#elif K_OS == K_OS_OS2
841 /* The page size on OS/2 wont change anytime soon. :-) */
842 return 0x1000;
843
844#elif K_OS == K_OS_WINDOWS
845 SYSTEM_INFO SysInfo;
846 GetSystemInfo(&SysInfo);
847 return SysInfo.dwPageSize;
848 /*return SysInfo.dwAllocationGranularity;*/
849#else
850# error "port me"
851#endif
852}
853
854
855/** @copydoc KRDROPS::pfnName */
856static const char *krdrFileName(PKRDR pRdr)
857{
858 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
859 return &pRdrFile->szFilename[0];
860}
861
862
863static KIPTR krdrFileNativeFH(PKRDR pRdr)
864{
865 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
866#if K_OS == K_OS_DARWIN \
867 || K_OS == K_OS_FREEBSD \
868 || K_OS == K_OS_LINUX \
869 || K_OS == K_OS_NETBSD \
870 || K_OS == K_OS_OPENBSD \
871 || K_OS == K_OS_OS2 \
872 || K_OS == K_OS_SOLARIS \
873 || K_OS == K_OS_WINDOWS
874 return (KIPTR)pRdrFile->File;
875#else
876# error "port me"
877#endif
878}
879
880
881/** @copydoc KRDROPS::pfnTell */
882static KFOFF krdrFileTell(PKRDR pRdr)
883{
884 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
885
886 /*
887 * If the offset is undefined, try figure out what it is.
888 */
889 if (pRdrFile->off == -1)
890 {
891#if K_OS == K_OS_DARWIN \
892 || K_OS == K_OS_FREEBSD \
893 || K_OS == K_OS_LINUX \
894 || K_OS == K_OS_NETBSD \
895 || K_OS == K_OS_OPENBSD \
896 || K_OS == K_OS_SOLARIS
897 pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_CUR, 0);
898 if (pRdrFile->off < 0)
899 pRdrFile->off = -1;
900
901#elif K_OS == K_OS_OS2
902 ULONG ulNew;
903 APIRET rc = DosSetFilePtr(pRdrFile->File, 0, FILE_CURRENT, &ulNew);
904 if (rc)
905 return -1;
906 pRdrFile->off = ulNew;
907
908#elif K_OS == K_OS_WINDOWS
909 LONG offHigh = 0;
910 LONG offLow;
911 int rc;
912
913 SetLastError(0);
914 offLow = SetFilePointer(pRdrFile->File, 0, &offHigh, FILE_CURRENT);
915 rc = GetLastError();
916 if (rc)
917 return -1;
918 pRdrFile->off = ((KFOFF)offHigh << 32) | offLow;
919
920#else
921# error "port me."
922#endif
923 }
924 return pRdrFile->off;
925}
926
927
928/** @copydoc KRDROPS::pfnSize */
929static KFOFF krdrFileSize(PKRDR pRdr)
930{
931 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
932 return pRdrFile->cb;
933}
934
935
936/** @copydoc KRDROPS::pfnAllUnmap */
937static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits)
938{
939 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
940
941 /* check for underflow */
942 if (pRdrFile->cMappings <= 0)
943 return KERR_INVALID_PARAMETER;
944
945 /* decrement usage counter, free mapping if no longer in use. */
946 if (!--pRdrFile->cMappings)
947 {
948 kHlpFree(pRdrFile->pvMapping);
949 pRdrFile->pvMapping = NULL;
950 }
951
952 return 0;
953}
954
955
956/** @copydoc KRDROPS::pfnAllMap */
957static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits)
958{
959 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
960
961 /*
962 * Do we need to map it?
963 */
964 if (!pRdrFile->pvMapping)
965 {
966 int rc;
967 KFOFF cbFile = pRdrFile->Core.pOps->pfnSize(pRdr);
968 KSIZE cb = (KSIZE)cbFile;
969 if (cb != cbFile)
970 return KERR_NO_MEMORY;
971
972 pRdrFile->pvMapping = kHlpAlloc(cb);
973 if (!pRdrFile->pvMapping)
974 return KERR_NO_MEMORY;
975 rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0);
976 if (rc)
977 {
978 kHlpFree(pRdrFile->pvMapping);
979 pRdrFile->pvMapping = NULL;
980 return rc;
981 }
982 pRdrFile->cMappings = 0;
983 }
984
985 *ppvBits = pRdrFile->pvMapping;
986 pRdrFile->cMappings++;
987 return 0;
988}
989
990
991/** @copydoc KRDROPS::pfnRead */
992static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
993{
994 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
995
996 /*
997 * Do a seek if needed.
998 */
999 if (pRdrFile->off != off)
1000 {
1001#if K_OS == K_OS_DARWIN \
1002 || K_OS == K_OS_FREEBSD \
1003 || K_OS == K_OS_LINUX \
1004 || K_OS == K_OS_NETBSD \
1005 || K_OS == K_OS_OPENBSD \
1006 || K_OS == K_OS_SOLARIS
1007 pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_SET, off);
1008 if (pRdrFile->off < 0)
1009 {
1010 int rc = (int)-pRdrFile->off;
1011 pRdrFile->off = -1;
1012 return -rc;
1013 }
1014
1015#elif K_OS == K_OS_OS2
1016 ULONG ulNew;
1017 APIRET rc;
1018
1019 rc = DosSetFilePtr(pRdrFile->File, off, FILE_BEGIN, &ulNew);
1020 if (rc)
1021 {
1022 pRdrFile->off = -1;
1023 return rc;
1024 }
1025
1026#elif K_OS == K_OS_WINDOWS
1027 LONG offHigh;
1028 LONG offLow;
1029
1030 offHigh = (LONG)(off >> 32);
1031 offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN);
1032 if ( offLow != (LONG)off
1033 || offHigh != (LONG)(off >> 32))
1034 {
1035 int rc = GetLastError();
1036 if (!rc)
1037 rc = KERR_GENERAL_FAILURE;
1038 pRdrFile->off = -1;
1039 return rc;
1040 }
1041
1042#else
1043# error "port me."
1044#endif
1045 }
1046
1047 /*
1048 * Do the read.
1049 */
1050#if K_OS == K_OS_DARWIN \
1051 || K_OS == K_OS_FREEBSD \
1052 || K_OS == K_OS_LINUX \
1053 || K_OS == K_OS_NETBSD \
1054 || K_OS == K_OS_OPENBSD \
1055 || K_OS == K_OS_SOLARIS
1056 {
1057 KSSIZE cbRead;
1058
1059 cbRead = kHlpSys_read(pRdrFile->File, pvBuf, cb);
1060 if (cbRead != cb)
1061 {
1062 pRdrFile->off = -1;
1063 if (cbRead < 0)
1064 return -cbRead;
1065 return KERR_GENERAL_FAILURE;
1066 }
1067 }
1068
1069#elif K_OS == K_OS_OS2
1070 {
1071 ULONG cbRead = 0;
1072 APIRET rc = DosRead(pRdrFile->File, pvBuf, cb, &cbRead);
1073 if (rc)
1074 {
1075 pRdrFile->off = -1;
1076 return rc;
1077 }
1078 if (cbRead != cb)
1079 {
1080 pRdrFile->off = -1;
1081 return KERR_GENERAL_FAILURE;
1082 }
1083 }
1084
1085#elif K_OS == K_OS_WINDOWS
1086 {
1087 DWORD cbRead = 0;
1088 if (!ReadFile(pRdrFile->File, pvBuf, cb, &cbRead, NULL))
1089 {
1090 int rc = GetLastError();
1091 if (!rc)
1092 rc = KERR_GENERAL_FAILURE;
1093 pRdrFile->off = -1;
1094 return rc;
1095 }
1096 if (cbRead != cb)
1097 {
1098 pRdrFile->off = -1;
1099 return KERR_GENERAL_FAILURE;
1100 }
1101 }
1102
1103#else
1104# error "port me."
1105#endif
1106
1107 pRdrFile->off = off + cb;
1108 return 0;
1109}
1110
1111
1112/** @copydoc KRDROPS::pfnDestroy */
1113static int krdrFileDestroy(PKRDR pRdr)
1114{
1115 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
1116 int rc;
1117
1118#if K_OS == K_OS_DARWIN \
1119 || K_OS == K_OS_FREEBSD \
1120 || K_OS == K_OS_LINUX \
1121 || K_OS == K_OS_NETBSD \
1122 || K_OS == K_OS_OPENBSD \
1123 || K_OS == K_OS_SOLARIS
1124 rc = kHlpSys_close(pRdrFile->File);
1125
1126#elif K_OS == K_OS_OS2
1127 rc = DosClose(pRdrFile->File);
1128
1129#elif K_OS == K_OS_WINDOWS
1130 rc = 0;
1131 if (!CloseHandle(pRdrFile->File))
1132 rc = GetLastError();
1133
1134#else
1135# error "port me"
1136#endif
1137
1138 if (pRdrFile->pvMapping)
1139 {
1140 kHlpFree(pRdrFile->pvMapping);
1141 pRdrFile->pvMapping = NULL;
1142 }
1143
1144 kHlpFree(pRdr);
1145 return rc;
1146}
1147
1148
1149/** @copydoc KRDROPS::pfnCreate */
1150static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename)
1151{
1152 KSIZE cchFilename;
1153 PKRDRFILE pRdrFile;
1154
1155 /*
1156 * Open the file, determin its size and correct filename.
1157 */
1158#if K_OS == K_OS_DARWIN \
1159 || K_OS == K_OS_FREEBSD \
1160 || K_OS == K_OS_LINUX \
1161 || K_OS == K_OS_NETBSD \
1162 || K_OS == K_OS_OPENBSD \
1163 || K_OS == K_OS_SOLARIS
1164 int File;
1165 KFOFF cb;
1166 KFOFF rc;
1167 char szFilename[1024];
1168
1169 cchFilename = kHlpStrLen(pszFilename);
1170 if (cchFilename >= sizeof(szFilename))
1171 return KERR_OUT_OF_RANGE;
1172 kHlpMemCopy(szFilename, pszFilename, cchFilename + 1);
1173 /** @todo normalize the filename. */
1174
1175# ifdef O_BINARY
1176 File = kHlpSys_open(pszFilename, O_RDONLY | O_BINARY, 0);
1177# else
1178 File = kHlpSys_open(pszFilename, O_RDONLY, 0);
1179# endif
1180 if (File < 0)
1181 return -File;
1182
1183 cb = kHlpSys_lseek(File, SEEK_END, 0);
1184 rc = kHlpSys_lseek(File, SEEK_SET, 0);
1185 if ( cb < 0
1186 || rc < 0)
1187 {
1188 kHlpSys_close(File);
1189 return cb < 0 ? -cb : -rc;
1190 }
1191
1192#elif K_OS == K_OS_OS2
1193 ULONG ulAction = 0;
1194 FILESTATUS3 Info;
1195 APIRET rc;
1196 HFILE File = 0;
1197 KFOFF cb;
1198 char szFilename[CCHMAXPATH];
1199
1200 if ((uintptr_t)pszFilename >= 0x20000000)
1201 {
1202 char *psz;
1203 cchFilename = kHlpStrLen(szFilename);
1204 psz = (char *)kHlpAllocA(cchFilename + 1);
1205 kHlpMemCopy(psz, pszFilename, cchFilename + 1);
1206 pszFilename = psz;
1207 }
1208 rc = DosOpen((PCSZ)pszFilename, &File, &ulAction, 0, FILE_NORMAL,
1209 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
1210 OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY | OPEN_FLAGS_RANDOMSEQUENTIAL,
1211 NULL);
1212 if (rc)
1213 return rc;
1214
1215 rc = DosQueryPathInfo((PCSZ)pszFilename, FIL_QUERYFULLNAME, szFilename, sizeof(szFilename));
1216 if (rc)
1217 {
1218 DosClose(File);
1219 return rc;
1220 }
1221
1222 rc = DosQueryFileInfo(File, FIL_STANDARD, &Info, sizeof(Info));
1223 if (rc)
1224 {
1225 DosClose(File);
1226 return rc;
1227 }
1228 cb = Info.cbFile;
1229
1230#elif K_OS == K_OS_WINDOWS
1231 SECURITY_ATTRIBUTES SecAttr;
1232 DWORD High;
1233 DWORD Low;
1234 int rc;
1235 HANDLE File;
1236 KFOFF cb;
1237 char szFilename[MAX_PATH];
1238
1239 SecAttr.bInheritHandle = FALSE;
1240 SecAttr.lpSecurityDescriptor = NULL;
1241 SecAttr.nLength = 0;
1242 File = CreateFile(pszFilename, GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, &SecAttr, OPEN_EXISTING, 0, NULL);
1243 if (File == INVALID_HANDLE_VALUE)
1244 return GetLastError();
1245
1246 if (!GetFullPathName(pszFilename, sizeof(szFilename), szFilename, NULL))
1247 {
1248 rc = GetLastError();
1249 CloseHandle(File);
1250 return rc;
1251 }
1252
1253 SetLastError(0);
1254 Low = GetFileSize(File, &High);
1255 rc = GetLastError();
1256 if (rc)
1257 {
1258 CloseHandle(File);
1259 return rc;
1260 }
1261 cb = ((KFOFF)High << 32) | Low;
1262
1263#else
1264# error "port me"
1265#endif
1266
1267
1268 /*
1269 * Allocate the reader instance.
1270 */
1271 cchFilename = kHlpStrLen(szFilename);
1272 pRdrFile = (PKRDRFILE)kHlpAlloc(sizeof(*pRdrFile) + cchFilename);
1273 if (!pRdrFile)
1274 {
1275#if K_OS == K_OS_DARWIN \
1276 || K_OS == K_OS_FREEBSD \
1277 || K_OS == K_OS_LINUX \
1278 || K_OS == K_OS_NETBSD \
1279 || K_OS == K_OS_OPENBSD \
1280 || K_OS == K_OS_SOLARIS
1281 kHlpSys_close(File);
1282#elif K_OS == K_OS_OS2
1283 DosClose(File);
1284#elif K_OS == K_OS_WINDOWS
1285 CloseHandle(File);
1286#else
1287# error "port me"
1288#endif
1289 return KERR_NO_MEMORY;
1290 }
1291
1292 /*
1293 * Initialize it and return successfully.
1294 */
1295 pRdrFile->Core.u32Magic = KRDR_MAGIC;
1296 pRdrFile->Core.pOps = &g_kRdrFileOps;
1297 pRdrFile->File = File;
1298 pRdrFile->cb = cb;
1299 pRdrFile->off = 0;
1300 pRdrFile->cPreps = 0;
1301 pRdrFile->cMappings = 0;
1302 pRdrFile->pvMapping = NULL;
1303 kHlpMemCopy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1);
1304
1305 *ppRdr = &pRdrFile->Core;
1306 return 0;
1307}
1308
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