VirtualBox

source: kStuff/trunk/kRdr/kRdrFile.cpp@ 28

Last change on this file since 28 was 2, checked in by bird, 17 years ago

Imported http://svn.netlabs.org/repos/libc/trunk/kStuff, revision 3612.

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

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