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

Last change on this file since 3600 was 3600, checked in by bird, 18 years ago

build on linux.

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