1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 map.cpp
12
13Abstract:
14
15 Implementation of file mapping API.
16
17
18
19--*/
20
21
22#include "pal/palinternal.h"
23#include "pal/dbgmsg.h"
24#include "pal/init.h"
25#include "pal/critsect.h"
26#include "pal/virtual.h"
27#include "pal/environ.h"
28#include "common.h"
29#include "pal/map.hpp"
30#include "pal/thread.hpp"
31#include "pal/file.hpp"
32#include "pal/malloc.hpp"
33
34#include <stddef.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/mman.h>
38#include <unistd.h>
39#include <errno.h>
40
41#include "rt/ntimage.h"
42#include <pal_endian.h>
43
44using namespace CorUnix;
45
46SET_DEFAULT_DEBUG_CHANNEL(VIRTUAL);
47
48#include "pal/utils.h"
49
50//
51// The mapping critical section guards access to the list
52// of currently mapped views. If a thread needs to access
53// both this critical section and the data for an object
54// it must acquire the object data first. That is, a thread
55// cannot acquire any other locks after taking hold of
56// this critical section.
57//
58
59CRITICAL_SECTION mapping_critsec;
60LIST_ENTRY MappedViewList;
61
62#ifndef CORECLR
63static PAL_ERROR MAPCreateTempFile(CPalThread *, PINT, PSZ);
64#endif // !CORECLR
65static PAL_ERROR MAPGrowLocalFile(INT, UINT);
66static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID );
67static PAL_ERROR MAPDesiredAccessAllowed( DWORD, DWORD, DWORD );
68
69static INT MAPProtectionToFileOpenFlags( DWORD );
70static BOOL MAPIsRequestPermissible( DWORD, CFileProcessLocalData * );
71static BOOL MAPContainsInvalidFlags( DWORD );
72static DWORD MAPConvertProtectToAccess( DWORD );
73static INT MAPFileMapToMmapFlags( DWORD );
74static DWORD MAPMmapProtToAccessFlags( int prot );
75#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
76static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
77 SIZE_T offset, long init_ref_count);
78static LONG NativeMapHolderAddRef(NativeMapHolder * thisPMH);
79static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisPMH);
80static PMAPPED_VIEW_LIST FindSharedMappingReplacement(CPalThread *pThread, dev_t deviceNum, ino_t inodeNum,
81 SIZE_T size, SIZE_T offset);
82#endif
83
84static PAL_ERROR
85MAPRecordMapping(
86 IPalObject *pMappingObject,
87 void *pPEBaseAddress,
88 void *addr,
89 size_t len,
90 int prot
91 );
92
93static PAL_ERROR
94MAPmmapAndRecord(
95 IPalObject *pMappingObject,
96 void *pPEBaseAddress,
97 void *addr,
98 size_t len,
99 int prot,
100 int flags,
101 int fd,
102 off_t offset,
103 LPVOID *ppvBaseAddress
104 );
105
106#if !HAVE_MMAP_DEV_ZERO
107/* We need MAP_ANON. However on some platforms like HP-UX, it is defined as MAP_ANONYMOUS */
108#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
109#define MAP_ANON MAP_ANONYMOUS
110#endif
111#endif
112
113void
114FileMappingCleanupRoutine(
115 CPalThread *pThread,
116 IPalObject *pObjectToCleanup,
117 bool fShutdown,
118 bool fCleanupSharedState
119 );
120
121PAL_ERROR
122FileMappingInitializationRoutine(
123 CPalThread *pThread,
124 CObjectType *pObjectType,
125 void *pImmutableData,
126 void *pSharedData,
127 void *pProcessLocalData
128 );
129
130void
131CFileMappingImmutableDataCopyRoutine(
132 void *pImmData,
133 void *pImmDataTarget
134 );
135
136void
137CFileMappingImmutableDataCleanupRoutine(
138 void *pImmData
139 );
140
141CObjectType CorUnix::otFileMapping(
142 otiFileMapping,
143 FileMappingCleanupRoutine,
144 FileMappingInitializationRoutine,
145 sizeof(CFileMappingImmutableData),
146 CFileMappingImmutableDataCopyRoutine,
147 CFileMappingImmutableDataCleanupRoutine,
148 sizeof(CFileMappingProcessLocalData),
149 NULL, // No process local data cleanup routine
150 0,
151 PAGE_READWRITE | PAGE_READONLY | PAGE_WRITECOPY,
152 CObjectType::SecuritySupported,
153 CObjectType::SecurityInfoNotPersisted,
154 CObjectType::UnnamedObject,
155 CObjectType::LocalDuplicationOnly,
156 CObjectType::UnwaitableObject,
157 CObjectType::SignalingNotApplicable,
158 CObjectType::ThreadReleaseNotApplicable,
159 CObjectType::OwnershipNotApplicable
160 );
161
162CAllowedObjectTypes aotFileMapping(otiFileMapping);
163
164void
165CFileMappingImmutableDataCopyRoutine(
166 void *pImmData,
167 void *pImmDataTarget
168 )
169{
170 PAL_ERROR palError = NO_ERROR;
171 CFileMappingImmutableData *pImmutableData = (CFileMappingImmutableData *) pImmData;
172 CFileMappingImmutableData *pImmutableDataTarget = (CFileMappingImmutableData *) pImmDataTarget;
173
174 if (NULL != pImmutableData->lpFileName)
175 {
176 pImmutableDataTarget->lpFileName = strdup(pImmutableData->lpFileName);
177 }
178}
179
180void
181CFileMappingImmutableDataCleanupRoutine(
182 void *pImmData
183 )
184{
185 PAL_ERROR palError = NO_ERROR;
186 CFileMappingImmutableData *pImmutableData = (CFileMappingImmutableData *) pImmData;
187
188 free(pImmutableData->lpFileName);
189}
190
191void
192FileMappingCleanupRoutine(
193 CPalThread *pThread,
194 IPalObject *pObjectToCleanup,
195 bool fShutdown,
196 bool fCleanupSharedState
197 )
198{
199 PAL_ERROR palError = NO_ERROR;
200 CFileMappingImmutableData *pImmutableData = NULL;
201 CFileMappingProcessLocalData *pLocalData = NULL;
202 IDataLock *pLocalDataLock = NULL;
203 bool fDataChanged = FALSE;
204
205 if (TRUE == fCleanupSharedState)
206 {
207 //
208 // If we created a temporary file to back this mapping we need
209 // to unlink it now
210 //
211
212 palError = pObjectToCleanup->GetImmutableData(
213 reinterpret_cast<void**>(&pImmutableData)
214 );
215
216 if (NO_ERROR != palError)
217 {
218 ASSERT("Unable to obtain immutable data for object to be reclaimed");
219 return;
220 }
221
222 if (pImmutableData->bPALCreatedTempFile)
223 {
224 unlink(pImmutableData->lpFileName);
225 }
226 }
227
228 if (FALSE == fShutdown)
229 {
230 //
231 // We only need to close the object's descriptor if we're not
232 // shutting down
233 //
234
235 palError = pObjectToCleanup->GetProcessLocalData(
236 pThread,
237 WriteLock,
238 &pLocalDataLock,
239 reinterpret_cast<void**>(&pLocalData)
240 );
241
242 if (NO_ERROR != palError)
243 {
244 ASSERT("Unable to obtain process local data for object to be reclaimed");
245 return;
246 }
247
248 if (-1 != pLocalData->UnixFd)
249 {
250 close(pLocalData->UnixFd);
251 pLocalData->UnixFd = -1;
252 fDataChanged = TRUE;
253 }
254
255 pLocalDataLock->ReleaseLock(pThread, fDataChanged);
256 }
257
258 //
259 // Why don't we need to deal with any views that may have been created
260 // from this mapping? If the process is shutting down then there's nothing
261 // that we need to take care of, as the OS will remove the underlying
262 // mappings when the process goes away. If we're not shutting down then
263 // there's no way for a view to exist against this mapping, since each
264 // view holds a reference against the mapping object.
265 //
266}
267
268PAL_ERROR
269FileMappingInitializationRoutine(
270 CPalThread *pThread,
271 CObjectType *pObjectType,
272 void *pvImmutableData,
273 void *pvSharedData,
274 void *pvProcessLocalData
275 )
276{
277 PAL_ERROR palError = NO_ERROR;
278
279 CFileMappingImmutableData *pImmutableData =
280 reinterpret_cast<CFileMappingImmutableData *>(pvImmutableData);
281 CFileMappingProcessLocalData *pProcessLocalData =
282 reinterpret_cast<CFileMappingProcessLocalData *>(pvProcessLocalData);
283
284 pProcessLocalData->UnixFd = InternalOpen(
285 pImmutableData->lpFileName,
286 MAPProtectionToFileOpenFlags(pImmutableData->flProtect) | O_CLOEXEC
287 );
288
289 if (-1 == pProcessLocalData->UnixFd)
290 {
291 palError = ERROR_INTERNAL_ERROR;
292 goto ExitFileMappingInitializationRoutine;
293 }
294
295#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
296 struct stat st;
297
298 if (0 == fstat(pProcessLocalData->UnixFd, &st))
299 {
300 pProcessLocalData->MappedFileDevNum = st.st_dev;
301 pProcessLocalData->MappedFileInodeNum = st.st_ino;
302 }
303 else
304 {
305 ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", pProcessLocalData->UnixFd);
306 }
307#endif
308
309ExitFileMappingInitializationRoutine:
310
311 return palError;
312}
313
314/*++
315Function:
316 CreateFileMappingA
317
318Note:
319 File mapping are used to do inter-process communication.
320
321See MSDN doc.
322--*/
323HANDLE
324PALAPI
325CreateFileMappingA(
326 IN HANDLE hFile,
327 IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
328 IN DWORD flProtect,
329 IN DWORD dwMaximumSizeHigh,
330 IN DWORD dwMaximumSizeLow,
331 IN LPCSTR lpName)
332{
333 HANDLE hFileMapping = NULL;
334 CPalThread *pThread = NULL;
335 PAL_ERROR palError = NO_ERROR;
336
337 PERF_ENTRY(CreateFileMappingA);
338 ENTRY("CreateFileMappingA(hFile=%p, lpAttributes=%p, flProtect=%#x, "
339 "dwMaxSizeH=%d, dwMaxSizeL=%d, lpName=%p (%s))\n",
340 hFile, lpFileMappingAttributes, flProtect,
341 dwMaximumSizeHigh, dwMaximumSizeLow,
342 lpName?lpName:"NULL",
343 lpName?lpName:"NULL");
344
345 pThread = InternalGetCurrentThread();
346
347 if (lpName != nullptr)
348 {
349 ASSERT("lpName: Cross-process named objects are not supported in PAL");
350 palError = ERROR_NOT_SUPPORTED;
351 }
352 else
353 {
354 palError = InternalCreateFileMapping(
355 pThread,
356 hFile,
357 lpFileMappingAttributes,
358 flProtect,
359 dwMaximumSizeHigh,
360 dwMaximumSizeLow,
361 NULL,
362 &hFileMapping
363 );
364 }
365
366
367 //
368 // We always need to set last error, even on success:
369 // we need to protect ourselves from the situation
370 // where last error is set to ERROR_ALREADY_EXISTS on
371 // entry to the function
372 //
373
374 pThread->SetLastError(palError);
375
376 LOGEXIT( "CreateFileMappingA returns HANDLE %p. \n", hFileMapping );
377 PERF_EXIT(CreateFileMappingA);
378 return hFileMapping;
379}
380
381/*++
382Function:
383 CreateFileMappingW
384
385Note:
386 File mapping are used to do inter-process communication.
387
388See MSDN doc.
389--*/
390HANDLE
391PALAPI
392CreateFileMappingW(
393 IN HANDLE hFile,
394 IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
395 IN DWORD flProtect,
396 IN DWORD dwMaximumSizeHigh,
397 IN DWORD dwMaximumSizeLow,
398 IN LPCWSTR lpName)
399{
400 HANDLE hFileMapping = NULL;
401 CPalThread *pThread = NULL;
402 PAL_ERROR palError = NO_ERROR;
403
404 PERF_ENTRY(CreateFileMappingW);
405 ENTRY("CreateFileMappingW(hFile=%p, lpAttributes=%p, flProtect=%#x, "
406 "dwMaxSizeH=%u, dwMaxSizeL=%u, lpName=%p (%S))\n",
407 hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh,
408 dwMaximumSizeLow, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
409
410 pThread = InternalGetCurrentThread();
411
412 palError = InternalCreateFileMapping(
413 pThread,
414 hFile,
415 lpFileMappingAttributes,
416 flProtect,
417 dwMaximumSizeHigh,
418 dwMaximumSizeLow,
419 lpName,
420 &hFileMapping
421 );
422
423 //
424 // We always need to set last error, even on success:
425 // we need to protect ourselves from the situation
426 // where last error is set to ERROR_ALREADY_EXISTS on
427 // entry to the function
428 //
429
430 pThread->SetLastError(palError);
431
432 LOGEXIT( "CreateFileMappingW returning %p .\n", hFileMapping );
433 PERF_EXIT(CreateFileMappingW);
434 return hFileMapping;
435}
436
437PAL_ERROR
438CorUnix::InternalCreateFileMapping(
439 CPalThread *pThread,
440 HANDLE hFile,
441 LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
442 DWORD flProtect,
443 DWORD dwMaximumSizeHigh,
444 DWORD dwMaximumSizeLow,
445 LPCWSTR lpName,
446 HANDLE *phMapping
447 )
448{
449 CObjectAttributes objectAttributes(lpName, lpFileMappingAttributes);
450 PAL_ERROR palError = NO_ERROR;
451 IPalObject *pMapping = NULL;
452 IPalObject *pRegisteredMapping = NULL;
453 CFileMappingProcessLocalData *pLocalData = NULL;
454 IDataLock *pLocalDataLock = NULL;
455 CFileMappingImmutableData *pImmutableData = NULL;
456 IPalObject *pFileObject = NULL;
457 CFileProcessLocalData *pFileLocalData = NULL;
458 IDataLock *pFileLocalDataLock = NULL;
459
460 struct stat UnixFileInformation;
461 INT UnixFd = -1;
462 BOOL bPALCreatedTempFile = FALSE;
463 UINT nFileSize = 0;
464
465 //
466 // Validate parameters
467 //
468
469 if (lpName != nullptr)
470 {
471 ASSERT("lpName: Cross-process named objects are not supported in PAL");
472 palError = ERROR_NOT_SUPPORTED;
473 goto ExitInternalCreateFileMapping;
474 }
475
476 if (0 != dwMaximumSizeHigh)
477 {
478 ASSERT("dwMaximumSizeHigh is always 0.\n");
479 palError = ERROR_INVALID_PARAMETER;
480 goto ExitInternalCreateFileMapping;
481 }
482
483 if (PAGE_READWRITE != flProtect
484 && PAGE_READONLY != flProtect
485 && PAGE_WRITECOPY != flProtect)
486 {
487 ASSERT( "invalid flProtect %#x, acceptable values are PAGE_READONLY "
488 "(%#x), PAGE_READWRITE (%#x) and PAGE_WRITECOPY (%#x).\n",
489 flProtect, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY );
490 palError = ERROR_INVALID_PARAMETER;
491 goto ExitInternalCreateFileMapping;
492 }
493
494 if (hFile == INVALID_HANDLE_VALUE && 0 == dwMaximumSizeLow)
495 {
496 ERROR( "If hFile is INVALID_HANDLE_VALUE, then you must specify a size.\n" );
497 palError = ERROR_INVALID_PARAMETER;
498 goto ExitInternalCreateFileMapping;
499 }
500
501 if (hFile != INVALID_HANDLE_VALUE && NULL != lpName)
502 {
503 ASSERT( "If hFile is not -1, then lpName must be NULL.\n" );
504 palError = ERROR_INVALID_PARAMETER;
505 goto ExitInternalCreateFileMapping;
506 }
507
508 palError = g_pObjectManager->AllocateObject(
509 pThread,
510 &otFileMapping,
511 &objectAttributes,
512 &pMapping
513 );
514
515 if (NO_ERROR != palError)
516 {
517 goto ExitInternalCreateFileMapping;
518 }
519
520 palError = pMapping->GetImmutableData(reinterpret_cast<void**>(&pImmutableData));
521 if (NO_ERROR != palError)
522 {
523 goto ExitInternalCreateFileMapping;
524 }
525
526 if (hFile == INVALID_HANDLE_VALUE
527#ifndef CORECLR
528 && NULL == lpName
529#endif // !CORECLR
530 )
531 {
532 //
533 // Note: this path is what prevents us supporting the
534 // duplication of file mapping objects across processes, since
535 // there is no backing file that the other process can open. We can
536 // avoid this restriction by always using a temp backing file for
537 // anonymous mappings.
538 //
539
540 /* Anonymous mapped files. */
541 _ASSERTE(pImmutableData->lpFileName == NULL);
542 pImmutableData->lpFileName = strdup("/dev/zero");
543 if (pImmutableData->lpFileName == NULL)
544 {
545 ASSERT("Unable to copy string\n");
546 palError = ERROR_INTERNAL_ERROR;
547 goto ExitInternalCreateFileMapping;
548 }
549
550#if HAVE_MMAP_DEV_ZERO
551
552 UnixFd = InternalOpen(pImmutableData->lpFileName, O_RDWR | O_CLOEXEC);
553 if ( -1 == UnixFd )
554 {
555 ERROR( "Unable to open the file.\n");
556 palError = ERROR_INTERNAL_ERROR;
557 goto ExitInternalCreateFileMapping;
558 }
559
560#else //!HAVE_MMAP_DEV_ZERO
561
562 UnixFd = -1; /* will pass MAP_ANON to mmap() instead */
563
564#endif //!HAVE_MMAP_DEV_ZERO
565
566 }
567 else
568 {
569 if ( hFile != INVALID_HANDLE_VALUE )
570 {
571 palError = g_pObjectManager->ReferenceObjectByHandle(
572 pThread,
573 hFile,
574 &aotFile,
575 GENERIC_READ,
576 &pFileObject
577 );
578
579 if (NO_ERROR != palError)
580 {
581 ERROR("Unable to obtain file data.\n");
582 palError = ERROR_INVALID_PARAMETER;
583 goto ExitInternalCreateFileMapping;
584 }
585
586 palError = pFileObject->GetProcessLocalData(
587 pThread,
588 ReadLock,
589 &pFileLocalDataLock,
590 reinterpret_cast<void**>(&pFileLocalData)
591 );
592
593 if (NO_ERROR != palError)
594 {
595 goto ExitInternalCreateFileMapping;
596 }
597
598 /* We need to check to ensure flProtect jives with
599 the permission on the file handle */
600 if (!MAPIsRequestPermissible(flProtect, pFileLocalData))
601 {
602 ERROR("File handle does not have the correct "
603 "permissions to create mapping\n" );
604 palError = ERROR_ACCESS_DENIED;
605 if (NULL != pFileLocalDataLock)
606 {
607 pFileLocalDataLock->ReleaseLock(pThread, FALSE);
608 }
609 goto ExitInternalCreateFileMapping;
610 }
611
612 //
613 // TODO: technically, the file mapping object should hold
614 // a reference to the passed in file object. This implementation
615 // only keeps the underlying native file structure (i.e., what
616 // the duplicated descriptors point to) open. There may be a risk
617 // here pertaining to the file lock information that the PAL must
618 // maintain (e.g,. if the passed in handle is closed immediately
619 // after the file mapping is opened then the lock information will
620 // be released, since we're not doing anything to keep it alive
621 // here).
622 //
623 // Having a direct reference to the underlying file object adds
624 // some complication, especially in cross-process cases. We may
625 // want to consider adding a reference to the PAL's file lock
626 // information, though...
627 //
628
629 UnixFd = fcntl(pFileLocalData->unix_fd, F_DUPFD_CLOEXEC, 0); // dup, but with CLOEXEC
630 if (-1 == UnixFd)
631 {
632 ERROR( "Unable to duplicate the Unix file descriptor!\n" );
633 palError = ERROR_INTERNAL_ERROR;
634 if (NULL != pFileLocalDataLock)
635 {
636 pFileLocalDataLock->ReleaseLock(pThread, FALSE);
637 }
638 goto ExitInternalCreateFileMapping;
639 }
640
641 _ASSERTE(pImmutableData->lpFileName == NULL);
642 pImmutableData->lpFileName = strdup(pFileLocalData->unix_filename);
643 if (pImmutableData->lpFileName == NULL)
644 {
645 ASSERT("Unable to copy string\n");
646 palError = ERROR_INTERNAL_ERROR;
647 if (NULL != pFileLocalDataLock)
648 {
649 pFileLocalDataLock->ReleaseLock(pThread, FALSE);
650 }
651 goto ExitInternalCreateFileMapping;
652 }
653
654 if (NULL != pFileLocalDataLock)
655 {
656 pFileLocalDataLock->ReleaseLock(pThread, FALSE);
657 }
658 }
659 else
660 {
661#ifndef CORECLR
662 TRACE( "INVALID_HANDLE_VALUE was the hFile, time to try to create a "
663 "temporary file" );
664
665 /* Create a temporary file on the filesystem in order to be
666 shared across processes. */
667 palError = MAPCreateTempFile(pThread, &UnixFd, pImmutableData->lpFileName);
668 if (NO_ERROR != palError)
669 {
670 ERROR("Unable to create the temporary file.\n");
671 goto ExitInternalCreateFileMapping;
672 }
673 bPALCreatedTempFile = TRUE;
674#else // !CORECLR
675 ASSERT("should not get here\n");
676 palError = ERROR_INTERNAL_ERROR;
677 goto ExitInternalCreateFileMapping;
678#endif // !CORECLR
679 }
680
681 if (-1 == fstat(UnixFd, &UnixFileInformation))
682 {
683 ASSERT("fstat() failed for this reason %s.\n", strerror(errno));
684 palError = ERROR_INTERNAL_ERROR;
685 goto ExitInternalCreateFileMapping;
686 }
687
688 if ( 0 == UnixFileInformation.st_size &&
689 0 == dwMaximumSizeHigh && 0 == dwMaximumSizeLow )
690 {
691 ERROR( "The file cannot be a zero length file.\n" );
692 palError = ERROR_FILE_INVALID;
693 goto ExitInternalCreateFileMapping;
694 }
695
696 if ( INVALID_HANDLE_VALUE != hFile &&
697 dwMaximumSizeLow > (DWORD) UnixFileInformation.st_size &&
698 ( PAGE_READONLY == flProtect || PAGE_WRITECOPY == flProtect ) )
699 {
700 /* In this situation, Windows returns an error, because the
701 permissions requested do not allow growing the file */
702 ERROR( "The file cannot be grown do to the map's permissions.\n" );
703 palError = ERROR_NOT_ENOUGH_MEMORY;
704 goto ExitInternalCreateFileMapping;
705 }
706
707 if ( (DWORD) UnixFileInformation.st_size < dwMaximumSizeLow )
708 {
709 TRACE( "Growing the size of file on disk to match requested size.\n" );
710
711 /* Need to grow the file on disk to match size. */
712 palError = MAPGrowLocalFile(UnixFd, dwMaximumSizeLow);
713 if (NO_ERROR != palError)
714 {
715 ERROR( "Unable to grow the file on disk.\n" );
716 goto ExitInternalCreateFileMapping;
717 }
718 }
719 }
720
721 nFileSize = ( 0 == dwMaximumSizeLow && 0 == dwMaximumSizeHigh ) ?
722 UnixFileInformation.st_size : dwMaximumSizeLow;
723
724 pImmutableData->MaxSize = nFileSize;
725 pImmutableData->flProtect = flProtect;
726 pImmutableData->bPALCreatedTempFile = bPALCreatedTempFile;
727 pImmutableData->dwDesiredAccessWhenOpened = MAPConvertProtectToAccess(flProtect);
728
729
730 //
731 // The local data isn't grabbed / modified until here so that we don't
732 // need to worry ourselves with locking issues with the passed in
733 // file handle -- all operations concerning the file handle are completed
734 // before we deal with the lock for the new object.
735 //
736
737 palError = pMapping->GetProcessLocalData(
738 pThread,
739 WriteLock,
740 &pLocalDataLock,
741 reinterpret_cast<void**>(&pLocalData)
742 );
743
744 if (NO_ERROR != palError)
745 {
746 goto ExitInternalCreateFileMapping;
747 }
748
749 pLocalData->UnixFd = UnixFd;
750
751#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
752 if (-1 == UnixFd)
753 {
754 pLocalData->MappedFileDevNum = (dev_t)-1; /* there is no standard NO_DEV */
755 pLocalData->MappedFileInodeNum = NO_INO;
756 }
757 else
758 {
759 struct stat st;
760
761 if (0 == fstat(UnixFd, &st))
762 {
763 pLocalData->MappedFileDevNum = st.st_dev;
764 pLocalData->MappedFileInodeNum = st.st_ino;
765 }
766 else
767 {
768 ERROR("Couldn't get inode info for fd=%d to be stored in mapping object\n", UnixFd);
769 palError = ERROR_INTERNAL_ERROR;
770 goto ExitInternalCreateFileMapping;
771 }
772 }
773#endif
774
775 pLocalDataLock->ReleaseLock(pThread, TRUE);
776 pLocalDataLock = NULL;
777
778 palError = g_pObjectManager->RegisterObject(
779 pThread,
780 pMapping,
781 &aotFileMapping,
782 flProtect, // TODO: is flProtect really an access right?
783 phMapping,
784 &pRegisteredMapping
785 );
786
787 //
788 // pMapping is invalidated by the call to RegisterObject, so NULL it
789 // out here to ensure that we don't try to release a reference on
790 // it down the line. This also ensures that we won't attempt to release
791 // any data associated with the mapping object here, as if any cleanup is
792 // necessary due to a failure in RegisterObject (which includes another
793 // object by the same name already existing) the cleanup will take place
794 // when that routine releases the reference to pMapping.
795 //
796
797 pMapping = NULL;
798
799ExitInternalCreateFileMapping:
800
801 if (NULL != pLocalDataLock)
802 {
803 pLocalDataLock->ReleaseLock(
804 pThread,
805 TRUE
806 );
807 }
808
809 if (NULL != pMapping)
810 {
811 pMapping->ReleaseReference(pThread);
812
813 if (bPALCreatedTempFile)
814 {
815 unlink(pImmutableData->lpFileName);
816 }
817
818 if (-1 != UnixFd)
819 {
820 close(UnixFd);
821 }
822 }
823
824 if (NULL != pRegisteredMapping)
825 {
826 pRegisteredMapping->ReleaseReference(pThread);
827 }
828
829 if (NULL != pFileObject)
830 {
831 pFileObject->ReleaseReference(pThread);
832 }
833
834 return palError;
835}
836
837/*++
838Function:
839 OpenFileMappingA
840
841See MSDN doc.
842--*/
843HANDLE
844PALAPI
845OpenFileMappingA(
846 IN DWORD dwDesiredAccess,
847 IN BOOL bInheritHandle,
848 IN LPCSTR lpName)
849{
850 HANDLE hFileMapping = NULL;
851 CPalThread *pThread = NULL;
852 PAL_ERROR palError = NO_ERROR;
853
854 PERF_ENTRY(OpenFileMappingA);
855 ENTRY("OpenFileMappingA(dwDesiredAccess=%u, bInheritHandle=%d, lpName=%p (%s)\n",
856 dwDesiredAccess, bInheritHandle, lpName?lpName:"NULL", lpName?lpName:"NULL");
857
858 pThread = InternalGetCurrentThread();
859
860 if (lpName == nullptr)
861 {
862 ERROR("name is NULL\n");
863 palError = ERROR_INVALID_PARAMETER;
864 }
865 else
866 {
867 ASSERT("lpName: Cross-process named objects are not supported in PAL");
868 palError = ERROR_NOT_SUPPORTED;
869 }
870
871 if (NO_ERROR != palError)
872 {
873 pThread->SetLastError(palError);
874 }
875 LOGEXIT( "OpenFileMappingA returning %p\n", hFileMapping );
876 PERF_EXIT(OpenFileMappingA);
877 return hFileMapping;
878}
879
880
881/*++
882Function:
883 OpenFileMappingW
884
885See MSDN doc.
886--*/
887HANDLE
888PALAPI
889OpenFileMappingW(
890 IN DWORD dwDesiredAccess,
891 IN BOOL bInheritHandle,
892 IN LPCWSTR lpName)
893{
894 HANDLE hFileMapping = NULL;
895 PAL_ERROR palError = NO_ERROR;
896 CPalThread *pThread = NULL;
897
898 PERF_ENTRY(OpenFileMappingW);
899 ENTRY("OpenFileMappingW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S)\n",
900 dwDesiredAccess, bInheritHandle, lpName?lpName:W16_NULLSTRING, lpName?lpName:W16_NULLSTRING);
901
902 pThread = InternalGetCurrentThread();
903
904 /* validate parameters */
905 if (lpName == nullptr)
906 {
907 ERROR("name is NULL\n");
908 palError = ERROR_INVALID_PARAMETER;
909 }
910 else
911 {
912 ASSERT("lpName: Cross-process named objects are not supported in PAL");
913 palError = ERROR_NOT_SUPPORTED;
914 }
915
916 if (NO_ERROR != palError)
917 {
918 pThread->SetLastError(palError);
919 }
920 LOGEXIT("OpenFileMappingW returning %p.\n", hFileMapping);
921 PERF_EXIT(OpenFileMappingW);
922 return hFileMapping;
923}
924
925/*++
926Function:
927 MapViewOfFile
928
929 Limitations: 1) Currently file mappings are supported only at file
930 offset 0.
931 2) Some platforms (specifically HP-UX) do not support
932 multiple simultaneous shared mapping of the same file
933 region in the same process. On these platforms, in case
934 we are asked for a new view completely contained in an
935 existing one, we return an address within the existing
936 mapping. In case the new requested view is overlapping
937 with the existing one, but not contained in it, the
938 mapping is impossible, and MapViewOfFile will fail.
939 Since currently the mappings are supported only at file
940 offset 0, MapViewOfFile will succeed if the new view
941 is equal or smaller of the existing one, and the address
942 returned will be the same address of the existing
943 mapping.
944 Since the underlying mapping is always the same, all
945 the shared views of the same file region will share the
946 same protection, i.e. they will have the largest
947 protection requested. If any mapping asked for a
948 read-write access, all the read-only mappings of the
949 same region will silently get a read-write access to
950 it.
951
952See MSDN doc.
953--*/
954LPVOID
955PALAPI
956MapViewOfFile(
957 IN HANDLE hFileMappingObject,
958 IN DWORD dwDesiredAccess,
959 IN DWORD dwFileOffsetHigh,
960 IN DWORD dwFileOffsetLow,
961 IN SIZE_T dwNumberOfBytesToMap)
962{
963 PAL_ERROR palError = NO_ERROR;
964 CPalThread *pThread = NULL;
965 LPVOID pvMappedBaseAddress = NULL;
966
967 PERF_ENTRY(MapViewOfFile);
968 ENTRY("MapViewOfFile(hFileMapping=%p, dwDesiredAccess=%u, "
969 "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u)\n",
970 hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
971 dwFileOffsetLow, dwNumberOfBytesToMap);
972
973 pThread = InternalGetCurrentThread();
974
975 palError = InternalMapViewOfFile(
976 pThread,
977 hFileMappingObject,
978 dwDesiredAccess,
979 dwFileOffsetHigh,
980 dwFileOffsetLow,
981 dwNumberOfBytesToMap,
982 &pvMappedBaseAddress
983 );
984
985 if (NO_ERROR != palError)
986 {
987 pThread->SetLastError(palError);
988 }
989
990 LOGEXIT( "MapViewOfFile returning %p.\n", pvMappedBaseAddress );
991 PERF_EXIT(MapViewOfFile);
992 return pvMappedBaseAddress;
993}
994
995LPVOID
996PALAPI
997MapViewOfFileEx(
998 IN HANDLE hFileMappingObject,
999 IN DWORD dwDesiredAccess,
1000 IN DWORD dwFileOffsetHigh,
1001 IN DWORD dwFileOffsetLow,
1002 IN SIZE_T dwNumberOfBytesToMap,
1003 IN LPVOID lpBaseAddress)
1004{
1005 PAL_ERROR palError = NO_ERROR;
1006 CPalThread *pThread = NULL;
1007 LPVOID pvMappedBaseAddress = NULL;
1008
1009 PERF_ENTRY(MapViewOfFileEx);
1010 ENTRY("MapViewOfFileEx(hFileMapping=%p, dwDesiredAccess=%u, "
1011 "dwFileOffsetH=%u, dwFileOffsetL=%u, dwNumberOfBytes=%u, lpBaseAddress=%p)\n",
1012 hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
1013 dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress);
1014
1015 pThread = InternalGetCurrentThread();
1016
1017 if (lpBaseAddress == NULL)
1018 {
1019 palError = InternalMapViewOfFile(
1020 pThread,
1021 hFileMappingObject,
1022 dwDesiredAccess,
1023 dwFileOffsetHigh,
1024 dwFileOffsetLow,
1025 dwNumberOfBytesToMap,
1026 &pvMappedBaseAddress
1027 );
1028
1029 if (NO_ERROR != palError)
1030 {
1031 pThread->SetLastError(palError);
1032 }
1033 }
1034 else
1035 {
1036 // TODO: Figure out if we can support mapping at a specific address on Linux.
1037 pThread->SetLastError(ERROR_INVALID_PARAMETER);
1038 }
1039
1040 LOGEXIT( "MapViewOfFileEx returning %p.\n", pvMappedBaseAddress );
1041 PERF_EXIT(MapViewOfFileEx);
1042 return pvMappedBaseAddress;
1043}
1044
1045
1046/*++
1047Function:
1048 UnmapViewOfFile
1049
1050See MSDN doc.
1051--*/
1052BOOL
1053PALAPI
1054UnmapViewOfFile(
1055 IN LPCVOID lpBaseAddress)
1056{
1057 PAL_ERROR palError;
1058 CPalThread *pThread;
1059
1060 PERF_ENTRY(UnmapViewOfFile);
1061 ENTRY("UnmapViewOfFile(lpBaseAddress=%p)\n", lpBaseAddress);
1062
1063 pThread = InternalGetCurrentThread();
1064
1065 palError = InternalUnmapViewOfFile(pThread, lpBaseAddress);
1066
1067 if (NO_ERROR != palError)
1068 {
1069 pThread->SetLastError(palError);
1070 }
1071
1072 LOGEXIT( "UnmapViewOfFile returning %s.\n", (NO_ERROR == palError) ? "TRUE" : "FALSE" );
1073 PERF_EXIT(UnmapViewOfFile);
1074 return (NO_ERROR == palError);
1075}
1076
1077PAL_ERROR
1078CorUnix::InternalMapViewOfFile(
1079 CPalThread *pThread,
1080 HANDLE hFileMappingObject,
1081 DWORD dwDesiredAccess,
1082 DWORD dwFileOffsetHigh,
1083 DWORD dwFileOffsetLow,
1084 SIZE_T dwNumberOfBytesToMap,
1085 LPVOID *ppvBaseAddress
1086 )
1087{
1088 PAL_ERROR palError = NO_ERROR;
1089 IPalObject *pMappingObject = NULL;
1090 CFileMappingImmutableData *pImmutableData = NULL;
1091 CFileMappingProcessLocalData *pProcessLocalData = NULL;
1092 IDataLock *pProcessLocalDataLock = NULL;
1093#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1094 PMAPPED_VIEW_LIST pReusedMapping = NULL;
1095#endif
1096 LPVOID pvBaseAddress = NULL;
1097
1098 /* Sanity checks */
1099 if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
1100 {
1101 ASSERT( "dwDesiredAccess can be one of FILE_MAP_WRITE, FILE_MAP_READ,"
1102 " FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
1103 palError = ERROR_INVALID_PARAMETER;
1104 goto InternalMapViewOfFileExit;
1105 }
1106
1107 if ( 0 != dwFileOffsetHigh || 0 != dwFileOffsetLow )
1108 {
1109 ASSERT( "dwFileOffsetHigh and dwFileOffsetLow are always 0.\n" );
1110 palError = ERROR_INVALID_PARAMETER;
1111 goto InternalMapViewOfFileExit;
1112 }
1113
1114 palError = g_pObjectManager->ReferenceObjectByHandle(
1115 pThread,
1116 hFileMappingObject,
1117 &aotFileMapping,
1118 dwDesiredAccess,
1119 &pMappingObject
1120 );
1121
1122 if (NO_ERROR != palError)
1123 {
1124 ERROR( "Unable to reference handle %p.\n",hFileMappingObject );
1125 goto InternalMapViewOfFileExit;
1126 }
1127
1128 palError = pMappingObject->GetImmutableData(
1129 reinterpret_cast<void**>(&pImmutableData)
1130 );
1131
1132 if (NO_ERROR != palError)
1133 {
1134 ERROR( "Unable to obtain object immutable data");
1135 goto InternalMapViewOfFileExit;
1136 }
1137
1138 palError = pMappingObject->GetProcessLocalData(
1139 pThread,
1140 ReadLock,
1141 &pProcessLocalDataLock,
1142 reinterpret_cast<void**>(&pProcessLocalData)
1143 );
1144
1145 if (NO_ERROR != palError)
1146 {
1147 ERROR( "Unable to obtain object process local data");
1148 goto InternalMapViewOfFileExit;
1149 }
1150
1151 /* If dwNumberOfBytesToMap is 0, we need to map the entire file.
1152 * mmap doesn't do the same thing as Windows in that case, though,
1153 * so we use the file size instead. */
1154 if (0 == dwNumberOfBytesToMap)
1155 {
1156 dwNumberOfBytesToMap = pImmutableData->MaxSize;
1157 }
1158
1159 palError = MAPDesiredAccessAllowed(
1160 pImmutableData->flProtect,
1161 dwDesiredAccess,
1162 pImmutableData->dwDesiredAccessWhenOpened
1163 );
1164
1165 if (NO_ERROR != palError)
1166 {
1167 goto InternalMapViewOfFileExit;
1168 }
1169
1170 InternalEnterCriticalSection(pThread, &mapping_critsec);
1171
1172 if (FILE_MAP_COPY == dwDesiredAccess)
1173 {
1174 int flags = MAP_PRIVATE;
1175
1176#if !HAVE_MMAP_DEV_ZERO
1177 if (pProcessLocalData->UnixFd == -1)
1178 {
1179 flags |= MAP_ANON;
1180 }
1181#endif
1182 pvBaseAddress = mmap(
1183 NULL,
1184 dwNumberOfBytesToMap,
1185 PROT_READ|PROT_WRITE,
1186 flags,
1187 pProcessLocalData->UnixFd,
1188 0
1189 );
1190 }
1191 else
1192 {
1193 INT prot = MAPFileMapToMmapFlags(dwDesiredAccess);
1194 if (prot != -1)
1195 {
1196 int flags = MAP_SHARED;
1197
1198#if !HAVE_MMAP_DEV_ZERO
1199 if (pProcessLocalData->UnixFd == -1)
1200 {
1201 flags |= MAP_ANON;
1202 }
1203#endif
1204
1205 pvBaseAddress = mmap(
1206 NULL,
1207 dwNumberOfBytesToMap,
1208 prot,
1209 flags,
1210 pProcessLocalData->UnixFd,
1211 0
1212 );
1213
1214#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1215 if ((MAP_FAILED == pvBaseAddress) && (ENOMEM == errno))
1216 {
1217 /* Search in list of MAPPED_MEMORY_INFO for a shared mapping
1218 with the same inode number
1219 */
1220 TRACE("Mmap() failed with errno=ENOMEM probably for multiple mapping "
1221 "limitation. Searching for a replacement among existing mappings\n");
1222
1223 pReusedMapping = FindSharedMappingReplacement(
1224 pThread,
1225 pProcessLocalData->MappedFileDevNum,
1226 pProcessLocalData->MappedFileInodeNum,
1227 dwNumberOfBytesToMap,
1228 0
1229 );
1230
1231 if (pReusedMapping)
1232 {
1233 int ret;
1234
1235 TRACE("Mapping @ %p {sz=%d offs=%d} fully "
1236 "contains the requested one {sz=%d offs=%d}: reusing it\n",
1237 pReusedMapping->pNMHolder->address,
1238 (int)pReusedMapping->pNMHolder->size,
1239 (int)pReusedMapping->pNMHolder->offset,
1240 dwNumberOfBytesToMap, 0);
1241
1242 /* Let's check the mapping's current protection */
1243 ret = mprotect(pReusedMapping->pNMHolder->address,
1244 pReusedMapping->pNMHolder->size,
1245 prot | PROT_CHECK);
1246 if (0 != ret)
1247 {
1248 /* We need to raise the protection to the desired
1249 one. That will give write access to any read-only
1250 mapping sharing this native mapping, but there is
1251 no way around this problem on systems that do not
1252 allow more than one mapping per file region, per
1253 process */
1254 TRACE("Raising protections on mapping @ %p to 0x%x\n",
1255 pReusedMapping->pNMHolder->address, prot);
1256 ret = mprotect(pReusedMapping->pNMHolder->address,
1257 pReusedMapping->pNMHolder->size,
1258 prot);
1259 }
1260
1261 if (ret != 0)
1262 {
1263 ERROR( "Failed setting protections on reused mapping\n");
1264
1265 NativeMapHolderRelease(pThread, pReusedMapping->pNMHolder);
1266 free(pReusedMapping);
1267 pReusedMapping = NULL;
1268 }
1269 }
1270 }
1271#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1272 }
1273 else
1274 {
1275 ASSERT( "MapFileMapToMmapFlags failed!\n" );
1276 palError = ERROR_INTERNAL_ERROR;
1277 goto InternalMapViewOfFileLeaveCriticalSection;
1278 }
1279 }
1280
1281 if (MAP_FAILED == pvBaseAddress
1282#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1283 && (pReusedMapping == NULL)
1284#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1285 )
1286 {
1287 ERROR( "mmap failed with code %s.\n", strerror( errno ) );
1288 palError = ERROR_NOT_ENOUGH_MEMORY;
1289 goto InternalMapViewOfFileLeaveCriticalSection;
1290
1291 }
1292
1293#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1294 if (pReusedMapping != NULL)
1295 {
1296 //
1297 // Add a reference to the file mapping object the reused mapping
1298 // points to (note that it may be different than the object this
1299 // call was actually made against) and add the view to the global
1300 // list. All other initialization took place in
1301 // FindSharedMappingReplacement
1302 //
1303
1304 pvBaseAddress = pReusedMapping->lpAddress;
1305 pReusedMapping->pFileMapping->AddReference();
1306 InsertTailList(&MappedViewList, &pReusedMapping->Link);
1307 }
1308 else
1309#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1310 {
1311 //
1312 // Allocate and fill out a new view structure, and add it to
1313 // the global list.
1314 //
1315
1316 PMAPPED_VIEW_LIST pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
1317 if (NULL != pNewView)
1318 {
1319 pNewView->lpAddress = pvBaseAddress;
1320 pNewView->NumberOfBytesToMap = dwNumberOfBytesToMap;
1321 pNewView->dwDesiredAccess = dwDesiredAccess;
1322 pNewView->pFileMapping = pMappingObject;
1323 pNewView->pFileMapping->AddReference();
1324 pNewView->lpPEBaseAddress = 0;
1325 InsertTailList(&MappedViewList, &pNewView->Link);
1326
1327#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1328 pNewView->MappedFileDevNum = pProcessLocalData->MappedFileDevNum;
1329 pNewView->MappedFileInodeNum = pProcessLocalData->MappedFileInodeNum;
1330
1331 pNewView->pNMHolder = NewNativeMapHolder(
1332 pThread,
1333 pvBaseAddress,
1334 dwNumberOfBytesToMap,
1335 0,
1336 1
1337 );
1338
1339 if (NULL == pNewView->pNMHolder)
1340 {
1341 pNewView->pFileMapping->ReleaseReference(pThread);
1342 RemoveEntryList(&pNewView->Link);
1343 free(pNewView);
1344 palError = ERROR_INTERNAL_ERROR;
1345 }
1346#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1347 }
1348 else
1349 {
1350 palError = ERROR_INTERNAL_ERROR;
1351 }
1352
1353 if (NO_ERROR != palError)
1354 {
1355 if (-1 == munmap(pvBaseAddress, dwNumberOfBytesToMap))
1356 {
1357 ERROR("Unable to unmap the file. Expect trouble.\n");
1358 goto InternalMapViewOfFileLeaveCriticalSection;
1359 }
1360 }
1361 }
1362
1363 if (NO_ERROR == palError)
1364 {
1365 TRACE( "Added %p to the list.\n", pvBaseAddress );
1366 *ppvBaseAddress = pvBaseAddress;
1367 }
1368
1369InternalMapViewOfFileLeaveCriticalSection:
1370
1371 InternalLeaveCriticalSection(pThread, &mapping_critsec);
1372
1373InternalMapViewOfFileExit:
1374
1375 if (NULL != pProcessLocalDataLock)
1376 {
1377 pProcessLocalDataLock->ReleaseLock(pThread, FALSE);
1378 }
1379
1380 if (NULL != pMappingObject)
1381 {
1382 pMappingObject->ReleaseReference(pThread);
1383 }
1384
1385 return palError;
1386}
1387
1388
1389PAL_ERROR
1390CorUnix::InternalUnmapViewOfFile(
1391 CPalThread *pThread,
1392 LPCVOID lpBaseAddress
1393 )
1394{
1395 PAL_ERROR palError = NO_ERROR;
1396 PMAPPED_VIEW_LIST pView = NULL;
1397 IPalObject *pMappingObject = NULL;
1398
1399 InternalEnterCriticalSection(pThread, &mapping_critsec);
1400
1401 pView = MAPGetViewForAddress(lpBaseAddress);
1402 if (NULL == pView)
1403 {
1404 ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
1405 palError = ERROR_INVALID_HANDLE;
1406 goto InternalUnmapViewOfFileExit;
1407 }
1408
1409#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1410 NativeMapHolderRelease(pThread, pView->pNMHolder);
1411 pView->pNMHolder = NULL;
1412#else
1413 if (-1 == munmap((LPVOID)lpBaseAddress, pView->NumberOfBytesToMap))
1414 {
1415 ASSERT( "Unable to unmap the memory. Error=%s.\n",
1416 strerror( errno ) );
1417 palError = ERROR_INTERNAL_ERROR;
1418
1419 //
1420 // Even if the unmap fails we want to continue removing the
1421 // info for this view
1422 //
1423 }
1424#endif
1425
1426 RemoveEntryList(&pView->Link);
1427 pMappingObject = pView->pFileMapping;
1428 free(pView);
1429
1430InternalUnmapViewOfFileExit:
1431
1432 InternalLeaveCriticalSection(pThread, &mapping_critsec);
1433
1434 //
1435 // We can't dereference the file mapping object until after
1436 // we've released the mapping critical section, since it may
1437 // start going down its cleanup path and we don't want to make
1438 // any assumptions as to what locks that might grab...
1439 //
1440
1441 if (NULL != pMappingObject)
1442 {
1443 pMappingObject->ReleaseReference(pThread);
1444 }
1445
1446 return palError;
1447}
1448
1449/*++
1450Function :
1451 MAPInitialize
1452
1453 Initialize the critical sections.
1454
1455Return value:
1456 TRUE if initialization succeeded
1457 FALSE otherwise
1458--*/
1459BOOL
1460MAPInitialize( void )
1461{
1462 TRACE( "Initialising the critical section.\n" );
1463
1464 InternalInitializeCriticalSection(&mapping_critsec);
1465
1466 InitializeListHead(&MappedViewList);
1467
1468 return TRUE;
1469}
1470
1471/*++
1472Function :
1473 MAPCleanup
1474
1475 Deletes the critical sections. And all other necessary cleanup.
1476
1477Note:
1478 This function is called after the handle manager is stopped. So
1479 there shouldn't be any call that will cause an access to the handle
1480 manager.
1481
1482--*/
1483void MAPCleanup( void )
1484{
1485 TRACE( "Deleting the critical section.\n" );
1486 InternalDeleteCriticalSection(&mapping_critsec);
1487}
1488
1489/*++
1490Function :
1491 MAPGetViewForAddress
1492
1493 Returns the mapped view (if any) that is based at the passed in address.
1494
1495 Callers to this function must hold mapping_critsec
1496--*/
1497static PMAPPED_VIEW_LIST MAPGetViewForAddress( LPCVOID lpAddress )
1498{
1499 if ( NULL == lpAddress )
1500 {
1501 ERROR( "lpAddress cannot be NULL\n" );
1502 return NULL;
1503 }
1504
1505 for(LIST_ENTRY *pLink = MappedViewList.Flink;
1506 pLink != &MappedViewList;
1507 pLink = pLink->Flink)
1508 {
1509 PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
1510
1511 if (pView->lpAddress == lpAddress)
1512 {
1513 return pView;
1514 }
1515 }
1516
1517 WARN( "No match found.\n" );
1518
1519 return NULL;
1520}
1521
1522/*++
1523Function :
1524
1525 MAPDesiredAccessAllowed
1526
1527 Determines if desired access is allowed based on the protection state.
1528
1529 if dwDesiredAccess conflicts with flProtect then the error is
1530 ERROR_INVALID_PARAMETER, if the dwDesiredAccess conflicts with
1531 dwDesiredAccessWhenOpened, then the error code is ERROR_ACCESS_DENIED
1532--*/
1533static PAL_ERROR MAPDesiredAccessAllowed( DWORD flProtect,
1534 DWORD dwUserDesiredAccess,
1535 DWORD dwDesiredAccessWhenOpened )
1536{
1537 TRACE( "flProtect=%d, dwUserDesiredAccess=%d, dwDesiredAccessWhenOpened=%d\n",
1538 flProtect, dwUserDesiredAccess, dwDesiredAccessWhenOpened );
1539
1540 /* check flProtect parameters*/
1541 if ( FILE_MAP_READ!= dwUserDesiredAccess && PAGE_READONLY == flProtect )
1542 {
1543 ERROR( "map object is read-only, can't map a view with write access\n");
1544 return ERROR_INVALID_PARAMETER;
1545 }
1546
1547 if ( FILE_MAP_WRITE == dwUserDesiredAccess && PAGE_READWRITE != flProtect )
1548 {
1549 ERROR( "map object not open read-write, can't map a view with write "
1550 "access.\n" );
1551 return ERROR_INVALID_PARAMETER;
1552 }
1553
1554 if ( FILE_MAP_COPY == dwUserDesiredAccess && PAGE_WRITECOPY != flProtect )
1555 {
1556 ERROR( "map object not open for copy-on-write, can't map copy-on-write "
1557 "view.\n" );
1558 return ERROR_INVALID_PARAMETER;
1559 }
1560
1561 /* Check to see we don't confict with the desired access we
1562 opened the mapping object with. */
1563 if ( ( dwUserDesiredAccess == FILE_MAP_READ ) &&
1564 !( ( dwDesiredAccessWhenOpened == FILE_MAP_READ ) ||
1565 ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
1566 {
1567 ERROR( "dwDesiredAccess conflict : read access requested, object not "
1568 "opened with read access.\n" );
1569 return ERROR_ACCESS_DENIED;
1570 }
1571 if ( ( dwUserDesiredAccess & FILE_MAP_WRITE ) &&
1572 !( ( dwDesiredAccessWhenOpened == FILE_MAP_WRITE ) ||
1573 ( dwDesiredAccessWhenOpened == FILE_MAP_ALL_ACCESS ) ) )
1574 {
1575 ERROR( "dwDesiredAccess conflict : write access requested, object not "
1576 "opened with write access.\n" );
1577 return ERROR_ACCESS_DENIED;
1578 }
1579 if ( ( dwUserDesiredAccess == FILE_MAP_COPY ) &&
1580 !( dwDesiredAccessWhenOpened == FILE_MAP_COPY ) )
1581 {
1582 ERROR( "dwDesiredAccess conflict : copy-on-write access requested, "
1583 "object not opened with copy-on-write access.\n" );
1584 return ERROR_ACCESS_DENIED;
1585 }
1586
1587 return NO_ERROR;
1588}
1589
1590/*++
1591Function :
1592 MAPConvertProtectToAccess
1593
1594 Converts the PAGE_READONLY type flags to FILE_MAP_READ flags.
1595
1596--*/
1597static DWORD MAPConvertProtectToAccess( DWORD flProtect )
1598{
1599 if ( PAGE_READONLY == flProtect )
1600 {
1601 return FILE_MAP_READ;
1602 }
1603 if ( PAGE_READWRITE == flProtect )
1604 {
1605 return FILE_MAP_ALL_ACCESS;
1606 }
1607 if ( PAGE_WRITECOPY == flProtect )
1608 {
1609 return FILE_MAP_COPY;
1610 }
1611
1612 ASSERT( "Unknown flag for flProtect. This line "
1613 "should not have been executed.\n " );
1614 return (DWORD) -1;
1615}
1616
1617/*++
1618Function :
1619 MAPConvertAccessToProtect
1620
1621 Converts the FILE_MAP_READ type flags to PAGE_READONLY flags.
1622 Currently, this function only deals with the access flags recognized as valid
1623 by MAPContainsInvalidFlags().
1624
1625--*/
1626static DWORD MAPConvertAccessToProtect(DWORD flAccess)
1627{
1628 if (flAccess == FILE_MAP_ALL_ACCESS)
1629 {
1630 return PAGE_READWRITE;
1631 }
1632 else if ((flAccess == FILE_MAP_COPY) || (flAccess == FILE_MAP_WRITE))
1633 {
1634 return PAGE_WRITECOPY;
1635 }
1636 else if (flAccess == FILE_MAP_READ)
1637 {
1638 return PAGE_READONLY;
1639 }
1640 else if (flAccess == 0)
1641 {
1642 return PAGE_NOACCESS;
1643 }
1644
1645 ASSERT("Unknown flag for flAccess.\n");
1646 return (DWORD) -1;
1647}
1648
1649/*++
1650Function :
1651 MAPFileMapToMmapFlags
1652
1653 Converts the mapping flags to unix protection flags.
1654--*/
1655static INT MAPFileMapToMmapFlags( DWORD flags )
1656{
1657 if ( FILE_MAP_READ == flags )
1658 {
1659 TRACE( "FILE_MAP_READ\n" );
1660 return PROT_READ;
1661 }
1662 else if ( FILE_MAP_WRITE == flags )
1663 {
1664 TRACE( "FILE_MAP_WRITE\n" );
1665 /* The limitation of x86 architecture
1666 means you cant have writable but not readable
1667 page. In Windows maps of FILE_MAP_WRITE can still be
1668 read from. */
1669 return PROT_WRITE | PROT_READ;
1670 }
1671 else if ( (FILE_MAP_READ|FILE_MAP_WRITE) == flags )
1672 {
1673 TRACE( "FILE_MAP_READ|FILE_MAP_WRITE\n" );
1674 return PROT_READ | PROT_WRITE;
1675 }
1676 else if( FILE_MAP_COPY == flags)
1677 {
1678 TRACE( "FILE_MAP_COPY\n");
1679 return PROT_READ | PROT_WRITE;
1680 }
1681
1682 ASSERT( "Unknown flag. This line should not have been executed.\n" );
1683 return -1;
1684}
1685
1686/*++
1687Function :
1688 MAPMmapProtToAccessFlags
1689
1690 Converts unix protection flags to file access flags.
1691 We ignore PROT_EXEC.
1692--*/
1693static DWORD MAPMmapProtToAccessFlags( int prot )
1694{
1695 DWORD flAccess = 0; // default: no access
1696
1697 if (PROT_NONE == prot)
1698 {
1699 flAccess = 0;
1700 }
1701 else if ( ((PROT_READ | PROT_WRITE) & prot) == (PROT_READ | PROT_WRITE) )
1702 {
1703 flAccess = FILE_MAP_ALL_ACCESS;
1704 }
1705 else if ( (PROT_WRITE & prot) == PROT_WRITE )
1706 {
1707 flAccess = FILE_MAP_WRITE;
1708 }
1709 else if ( (PROT_READ & prot) == PROT_READ )
1710 {
1711 flAccess = FILE_MAP_READ;
1712 }
1713 else
1714 {
1715 ASSERT( "Unknown Unix protection flag\n" );
1716 }
1717
1718 return flAccess;
1719}
1720
1721/*++
1722Function :
1723
1724 MAPGrowLocalFile
1725
1726 Grows the file on disk to match the specified size.
1727
1728--*/
1729static PAL_ERROR MAPGrowLocalFile( INT UnixFD, UINT NewSize )
1730{
1731 PAL_ERROR palError = NO_ERROR;
1732 INT TruncateRetVal = -1;
1733 struct stat FileInfo;
1734 TRACE( "Entered MapGrowLocalFile (UnixFD=%d,NewSize%d)\n", UnixFD, NewSize );
1735
1736 //
1737 // TODO: can we add configure flags to model the behavior of ftruncate
1738 // among our various target platforms? How much would that actually gain
1739 // us?
1740 //
1741
1742 /* ftruncate is a standard function, but the behavior of enlarging files is
1743 non-standard. So I will try to enlarge a file, and if that fails try the
1744 less efficent way.*/
1745 TruncateRetVal = ftruncate( UnixFD, NewSize );
1746 fstat( UnixFD, &FileInfo );
1747
1748 if ( TruncateRetVal != 0 || FileInfo.st_size != (int) NewSize )
1749 {
1750 INT OrigSize;
1751 CONST UINT BUFFER_SIZE = 128;
1752 BYTE buf[BUFFER_SIZE];
1753 UINT x = 0;
1754 UINT CurrentPosition = 0;
1755
1756 TRACE( "Trying the less efficent way.\n" );
1757
1758 CurrentPosition = lseek( UnixFD, 0, SEEK_CUR );
1759 OrigSize = lseek( UnixFD, 0, SEEK_END );
1760 if ( OrigSize == -1 )
1761 {
1762 ERROR( "Unable to locate the EOF marker. Reason=%s\n",
1763 strerror( errno ) );
1764 palError = ERROR_INTERNAL_ERROR;
1765 goto done;
1766 }
1767
1768 if (NewSize <= (UINT) OrigSize)
1769 {
1770 return TRUE;
1771 }
1772
1773 memset( buf, 0, BUFFER_SIZE );
1774
1775 for ( x = 0; x < NewSize - OrigSize - BUFFER_SIZE; x += BUFFER_SIZE )
1776 {
1777 if ( write( UnixFD, (LPVOID)buf, BUFFER_SIZE ) == -1 )
1778 {
1779 ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
1780 if((errno == ENOSPC) || (errno == EDQUOT))
1781 {
1782 palError = ERROR_DISK_FULL;
1783 }
1784 else
1785 {
1786 palError = ERROR_INTERNAL_ERROR;
1787 }
1788 goto done;
1789 }
1790 }
1791 /* Catch any left overs. */
1792 if ( x != NewSize )
1793 {
1794 if ( write( UnixFD, (LPVOID)buf, NewSize - OrigSize - x) == -1 )
1795 {
1796 ERROR( "Unable to grow the file. Reason=%s\n", strerror( errno ) );
1797 if((errno == ENOSPC) || (errno == EDQUOT))
1798 {
1799 palError = ERROR_DISK_FULL;
1800 }
1801 else
1802 {
1803 palError = ERROR_INTERNAL_ERROR;
1804 }
1805 goto done;
1806 }
1807 }
1808
1809 /* restore the file pointer position */
1810 lseek( UnixFD, CurrentPosition, SEEK_SET );
1811 }
1812
1813done:
1814 return palError;
1815}
1816
1817/*++
1818Function :
1819 MAPContainsInvalidFlags
1820
1821 Checks that only valid flags are in the parameter.
1822
1823--*/
1824static BOOL MAPContainsInvalidFlags( DWORD flags )
1825{
1826
1827 if ( (flags == FILE_MAP_READ) ||
1828 (flags == FILE_MAP_WRITE) ||
1829 (flags == FILE_MAP_ALL_ACCESS) ||
1830 (flags == FILE_MAP_COPY) )
1831 {
1832 return FALSE;
1833 }
1834 else
1835 {
1836 return TRUE;
1837 }
1838}
1839
1840/*++
1841Function :
1842 MAPProtectionToFileOpenFlags
1843
1844 Converts the PAGE_* flags to the O_* flags.
1845
1846 Returns the file open flags.
1847--*/
1848static INT MAPProtectionToFileOpenFlags( DWORD flProtect )
1849{
1850 INT retVal = 0;
1851 switch(flProtect)
1852 {
1853 case PAGE_READONLY:
1854 retVal = O_RDONLY;
1855 break;
1856 case PAGE_READWRITE:
1857 retVal = O_RDWR;
1858 break;
1859 case PAGE_WRITECOPY:
1860 retVal = O_RDONLY;
1861 break;
1862 default:
1863 ASSERT("unexpected flProtect value %#x\n", flProtect);
1864 retVal = 0;
1865 break;
1866 }
1867 return retVal;
1868}
1869
1870/*++
1871Function :
1872
1873 MAPIsRequestPermissible
1874
1875 DWORD flProtect - The requested file mapping protection .
1876 file * pFileStruct - The file structure containing all the information.
1877
1878--*/
1879static BOOL MAPIsRequestPermissible( DWORD flProtect, CFileProcessLocalData * pFileLocalData )
1880{
1881 if ( ( (flProtect == PAGE_READONLY || flProtect == PAGE_WRITECOPY) &&
1882 (pFileLocalData->open_flags_deviceaccessonly == TRUE ||
1883 pFileLocalData->open_flags & O_WRONLY) )
1884 )
1885 {
1886 /*
1887 * PAGE_READONLY or PAGE_WRITECOPY access to a file must at least be
1888 * readable. Contrary to what MSDN says, PAGE_WRITECOPY
1889 * only needs to be readable.
1890 */
1891 return FALSE;
1892 }
1893 else if ( flProtect == PAGE_READWRITE && !(pFileLocalData->open_flags & O_RDWR) )
1894 {
1895 /*
1896 * PAGE_READWRITE access to a file needs to be readable and writable
1897 */
1898 return FALSE;
1899 }
1900 else
1901 {
1902 /* Action is permissible */
1903 return TRUE;
1904 }
1905}
1906
1907// returns TRUE if we have information about the specified address
1908BOOL MAPGetRegionInfo(LPVOID lpAddress,
1909 PMEMORY_BASIC_INFORMATION lpBuffer)
1910{
1911 BOOL fFound = FALSE;
1912 CPalThread * pThread = InternalGetCurrentThread();
1913
1914 InternalEnterCriticalSection(pThread, &mapping_critsec);
1915
1916 for(LIST_ENTRY *pLink = MappedViewList.Flink;
1917 pLink != &MappedViewList;
1918 pLink = pLink->Flink)
1919 {
1920 UINT MappedSize;
1921 VOID * real_map_addr;
1922 SIZE_T real_map_sz;
1923 PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
1924
1925#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1926 real_map_addr = pView->pNMHolder->address;
1927 real_map_sz = pView->pNMHolder->size;
1928#else
1929 real_map_addr = pView->lpAddress;
1930 real_map_sz = pView->NumberOfBytesToMap;
1931#endif
1932
1933 MappedSize = ALIGN_UP(real_map_sz, GetVirtualPageSize());
1934 if ( real_map_addr <= lpAddress &&
1935 (VOID *)((UINT_PTR)real_map_addr+MappedSize) > lpAddress )
1936 {
1937 if (lpBuffer)
1938 {
1939 SIZE_T regionSize = MappedSize + (UINT_PTR) real_map_addr -
1940 ALIGN_DOWN((UINT_PTR)lpAddress, GetVirtualPageSize());
1941
1942 lpBuffer->BaseAddress = lpAddress;
1943 lpBuffer->AllocationProtect = 0;
1944 lpBuffer->RegionSize = regionSize;
1945 lpBuffer->State = MEM_COMMIT;
1946 lpBuffer->Protect = MAPConvertAccessToProtect(pView->dwDesiredAccess);
1947 lpBuffer->Type = MEM_MAPPED;
1948 }
1949
1950 fFound = TRUE;
1951 break;
1952 }
1953 }
1954
1955 InternalLeaveCriticalSection(pThread, &mapping_critsec);
1956
1957 return fFound;
1958}
1959
1960#if ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
1961
1962//
1963// Callers of FindSharedMappingReplacement must hold mapping_critsec
1964//
1965
1966static PMAPPED_VIEW_LIST FindSharedMappingReplacement(
1967 CPalThread *pThread,
1968 dev_t deviceNum,
1969 ino_t inodeNum,
1970 SIZE_T size,
1971 SIZE_T offset)
1972{
1973 PMAPPED_VIEW_LIST pNewView = NULL;
1974
1975 if (size == 0)
1976 {
1977 ERROR("Mapping size cannot be NULL\n");
1978 return NULL;
1979 }
1980
1981 for (LIST_ENTRY *pLink = MappedViewList.Flink;
1982 pLink != &MappedViewList;
1983 pLink = pLink->Flink)
1984 {
1985 PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
1986
1987 if (pView->MappedFileDevNum != deviceNum
1988 || pView->MappedFileInodeNum != inodeNum
1989 || pView->dwDesiredAccess == FILE_MAP_COPY)
1990 {
1991 continue;
1992 }
1993
1994 //
1995 // This is a shared mapping for the same indoe / device. Now, check
1996 // to see if it overlaps with the range for the new view
1997 //
1998
1999 SIZE_T real_map_offs = pView->pNMHolder->offset;
2000 SIZE_T real_map_sz = pView->pNMHolder->size;
2001
2002 if (real_map_offs <= offset
2003 && real_map_offs+real_map_sz >= offset)
2004 {
2005 //
2006 // The views overlap. Even if this view is not reusable for the
2007 // new once the search is over, as on
2008 // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS systems there
2009 // cannot be shared mappings of two overlapping regions of the
2010 // same file, in the same process. Therefore, whether this view
2011 // is reusable or not we cannot mmap the requested region of
2012 // the specified file.
2013 //
2014
2015 if (real_map_offs+real_map_sz >= offset+size)
2016 {
2017 /* The new desired mapping is fully contained in the
2018 one just found: we can reuse this one */
2019
2020 pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(MAPPED_VIEW_LIST));
2021 if (pNewView)
2022 {
2023 memcpy(pNewView, pView, sizeof(*pNewView));
2024 NativeMapHolderAddRef(pNewView->pNMHolder);
2025 pNewView->lpAddress = (VOID*)((CHAR*)pNewView->pNMHolder->address +
2026 offset - pNewView->pNMHolder->offset);
2027 pNewView->NumberOfBytesToMap = size;
2028 }
2029 else
2030 {
2031 ERROR("No memory for new MAPPED_VIEW_LIST node\n");
2032 }
2033 }
2034
2035 break;
2036 }
2037 }
2038
2039 TRACE ("FindSharedMappingReplacement returning %p\n", pNewView);
2040 return pNewView;
2041}
2042
2043static NativeMapHolder * NewNativeMapHolder(CPalThread *pThread, LPVOID address, SIZE_T size,
2044 SIZE_T offset, long init_ref_count)
2045{
2046 NativeMapHolder * pThisMapHolder;
2047
2048 if (init_ref_count < 0)
2049 {
2050 ASSERT("Negative initial reference count for new map holder\n");
2051 return NULL;
2052 }
2053
2054 pThisMapHolder =
2055 (NativeMapHolder *)InternalMalloc(sizeof(NativeMapHolder));
2056
2057 if (pThisMapHolder)
2058 {
2059 pThisMapHolder->ref_count = init_ref_count;
2060 pThisMapHolder->address = address;
2061 pThisMapHolder->size = size;
2062 pThisMapHolder->offset = offset;
2063 }
2064
2065 return pThisMapHolder;
2066}
2067
2068static LONG NativeMapHolderAddRef(NativeMapHolder * thisNMH)
2069{
2070 LONG ret = InterlockedIncrement(&thisNMH->ref_count);
2071 return ret;
2072}
2073
2074static LONG NativeMapHolderRelease(CPalThread *pThread, NativeMapHolder * thisNMH)
2075{
2076 LONG ret = InterlockedDecrement(&thisNMH->ref_count);
2077 if (ret == 0)
2078 {
2079 if (-1 == munmap(thisNMH->address, thisNMH->size))
2080 {
2081 ASSERT( "Unable to unmap memory. Error=%s.\n",
2082 strerror( errno ) );
2083 }
2084 else
2085 {
2086 TRACE( "Successfully unmapped %p (size=%lu)\n",
2087 thisNMH->address, (unsigned long)thisNMH->size);
2088 }
2089 free (thisNMH);
2090 }
2091 else if (ret < 0)
2092 {
2093 ASSERT( "Negative reference count for map holder %p"
2094 " {address=%p, size=%lu}\n", thisNMH->address,
2095 (unsigned long)thisNMH->size);
2096 }
2097
2098 return ret;
2099}
2100
2101#endif // ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS
2102
2103// Record a mapping in the MappedViewList list.
2104// This call assumes the mapping_critsec has already been taken.
2105static PAL_ERROR
2106MAPRecordMapping(
2107 IPalObject *pMappingObject,
2108 void *pPEBaseAddress,
2109 void *addr,
2110 size_t len,
2111 int prot
2112 )
2113{
2114 if (pPEBaseAddress == NULL)
2115 {
2116 return ERROR_INTERNAL_ERROR;
2117 }
2118
2119 PAL_ERROR palError = NO_ERROR;
2120 PMAPPED_VIEW_LIST pNewView;
2121 pNewView = (PMAPPED_VIEW_LIST)InternalMalloc(sizeof(*pNewView));
2122 if (NULL != pNewView)
2123 {
2124 pNewView->lpAddress = addr;
2125 pNewView->NumberOfBytesToMap = len;
2126 pNewView->dwDesiredAccess = MAPMmapProtToAccessFlags(prot);
2127 pMappingObject->AddReference();
2128 pNewView->pFileMapping = pMappingObject;
2129 pNewView->lpPEBaseAddress = pPEBaseAddress;
2130 InsertTailList(&MappedViewList, &pNewView->Link);
2131
2132 TRACE_(LOADER)("Added address %p, size 0x%x, to the mapped file list.\n", addr, len);
2133 }
2134 else
2135 {
2136 palError = ERROR_INTERNAL_ERROR;
2137 }
2138
2139 return palError;
2140}
2141
2142// Do the actual mmap() call, and record the mapping in the MappedViewList list.
2143// This call assumes the mapping_critsec has already been taken.
2144static PAL_ERROR
2145MAPmmapAndRecord(
2146 IPalObject *pMappingObject,
2147 void *pPEBaseAddress,
2148 void *addr,
2149 size_t len,
2150 int prot,
2151 int flags,
2152 int fd,
2153 off_t offset,
2154 LPVOID *ppvBaseAddress
2155 )
2156{
2157 _ASSERTE(pPEBaseAddress != NULL);
2158
2159 PAL_ERROR palError = NO_ERROR;
2160 LPVOID pvBaseAddress = NULL;
2161
2162 off_t adjust = offset & (GetVirtualPageSize() - 1);
2163
2164 pvBaseAddress = mmap(static_cast<char *>(addr) - adjust, len + adjust, prot, flags, fd, offset - adjust);
2165 if (MAP_FAILED == pvBaseAddress)
2166 {
2167 ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
2168 palError = FILEGetLastErrorFromErrno();
2169 }
2170 else
2171 {
2172 palError = MAPRecordMapping(pMappingObject, pPEBaseAddress, pvBaseAddress, len, prot);
2173 if (NO_ERROR != palError)
2174 {
2175 if (-1 == munmap(pvBaseAddress, len))
2176 {
2177 ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
2178 }
2179 }
2180 else
2181 {
2182 *ppvBaseAddress = pvBaseAddress;
2183 }
2184 }
2185
2186 return palError;
2187}
2188
2189/*++
2190 MAPMapPEFile -
2191
2192 Map a PE format file into memory like Windows LoadLibrary() would do.
2193 Doesn't apply base relocations if the function is relocated.
2194
2195Parameters:
2196 IN hFile - file to map
2197
2198Return value:
2199 non-NULL - the base address of the mapped image
2200 NULL - error, with last error set.
2201--*/
2202
2203void * MAPMapPEFile(HANDLE hFile)
2204{
2205 PAL_ERROR palError = 0;
2206 IPalObject *pFileObject = NULL;
2207 IDataLock *pLocalDataLock = NULL;
2208 CFileProcessLocalData *pLocalData = NULL;
2209 CPalThread *pThread = InternalGetCurrentThread();
2210 void * loadedBase = NULL;
2211 IMAGE_DOS_HEADER * loadedHeader = NULL;
2212 void * retval;
2213#if _DEBUG
2214 bool forceRelocs = false;
2215 char* envVar;
2216#endif
2217
2218 ENTRY("MAPMapPEFile (hFile=%p)\n", hFile);
2219
2220 //Step 0: Verify values, find internal pal data structures.
2221 if (INVALID_HANDLE_VALUE == hFile)
2222 {
2223 ERROR_(LOADER)( "Invalid file handle\n" );
2224 palError = ERROR_INVALID_HANDLE;
2225 goto done;
2226 }
2227
2228 palError = g_pObjectManager->ReferenceObjectByHandle(
2229 pThread,
2230 hFile,
2231 &aotFile,
2232 GENERIC_READ,
2233 &pFileObject
2234 );
2235 if (NO_ERROR != palError)
2236 {
2237 ERROR_(LOADER)( "ReferenceObjectByHandle failed\n" );
2238 goto done;
2239 }
2240
2241 palError = pFileObject->GetProcessLocalData(
2242 pThread,
2243 ReadLock,
2244 &pLocalDataLock,
2245 reinterpret_cast<void**>(&pLocalData)
2246 );
2247 if (NO_ERROR != palError)
2248 {
2249 ERROR_(LOADER)( "GetProcessLocalData failed\n" );
2250 goto done;
2251 }
2252
2253 int fd;
2254 fd = pLocalData->unix_fd;
2255 //Step 1: Read the PE headers and reserve enough space for the whole image somewhere.
2256 IMAGE_DOS_HEADER dosHeader;
2257 IMAGE_NT_HEADERS ntHeader;
2258 errno = 0;
2259 if (0 != lseek(fd, 0, SEEK_SET))
2260 {
2261 palError = FILEGetLastErrorFromErrno();
2262 ERROR_(LOADER)( "lseek failed\n" );
2263 goto done;
2264 }
2265 if (sizeof(dosHeader) != read(fd, &dosHeader, sizeof(dosHeader)))
2266 {
2267 palError = FILEGetLastErrorFromErrno();
2268 ERROR_(LOADER)( "reading dos header failed\n" );
2269 goto done;
2270 }
2271 if (dosHeader.e_lfanew != lseek(fd, dosHeader.e_lfanew, SEEK_SET))
2272 {
2273 palError = FILEGetLastErrorFromErrno();
2274 goto done;
2275 }
2276 if (sizeof(ntHeader) != read(fd, &ntHeader, sizeof(ntHeader)))
2277 {
2278 palError = FILEGetLastErrorFromErrno();
2279 goto done;
2280 }
2281
2282 if ((VAL16(IMAGE_DOS_SIGNATURE) != VAL16(dosHeader.e_magic))
2283 || (VAL32(IMAGE_NT_SIGNATURE) != VAL32(ntHeader.Signature))
2284 || (VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC) != VAL16(ntHeader.OptionalHeader.Magic) ) )
2285 {
2286 ERROR_(LOADER)( "Magic number mismatch\n" );
2287 palError = ERROR_INVALID_PARAMETER;
2288 goto done;
2289 }
2290
2291 //This doesn't read the entire NT header (the optional header technically has a variable length. But I
2292 //don't need more directories.
2293
2294 //I now know how big the file is. Reserve enough address space for the whole thing. Try to get the
2295 //preferred base. Create the intial mapping as "no access". We'll use that for the guard pages in the
2296 //"holes" between sections.
2297 SIZE_T preferredBase, virtualSize;
2298 preferredBase = ntHeader.OptionalHeader.ImageBase;
2299 virtualSize = ntHeader.OptionalHeader.SizeOfImage;
2300
2301 // Validate the image header
2302 if ( (preferredBase == 0)
2303 || (virtualSize == 0)
2304 || (preferredBase + virtualSize < preferredBase) // Does the image overflow?
2305 )
2306 {
2307 ERROR_(LOADER)( "image is corrupt\n" );
2308 palError = ERROR_INVALID_PARAMETER;
2309 goto done;
2310 }
2311
2312#if _DEBUG
2313 envVar = EnvironGetenv("PAL_ForceRelocs");
2314 if (envVar)
2315 {
2316 if (strlen(envVar) > 0)
2317 {
2318 forceRelocs = true;
2319 TRACE_(LOADER)("Forcing rebase of image\n");
2320 }
2321
2322 free(envVar);
2323 }
2324
2325 void * pForceRelocBase;
2326 pForceRelocBase = NULL;
2327 if (forceRelocs)
2328 {
2329 //if we're forcing relocs, create an anonymous mapping at the preferred base. Only create the
2330 //mapping if we can create it at the specified address.
2331 pForceRelocBase = mmap( (void*)preferredBase, GetVirtualPageSize(), PROT_NONE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0 );
2332 if (pForceRelocBase == MAP_FAILED)
2333 {
2334 TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed\n", (void*)preferredBase);
2335 forceRelocs = false;
2336 }
2337 else if ((void*)preferredBase != pForceRelocBase)
2338 {
2339 TRACE_(LOADER)("Attempt to take preferred base of %p to force relocation failed; actually got %p\n", (void*)preferredBase, pForceRelocBase);
2340 }
2341 }
2342#endif // _DEBUG
2343
2344 // The first mmap mapping covers the entire file but just reserves space. Subsequent mappings cover
2345 // individual parts of the file, and actually map pages in. Note that according to the mmap() man page, "A
2346 // successful mmap deletes any previous mapping in the allocated address range." Also, "If a MAP_FIXED
2347 // request is successful, the mapping established by mmap() replaces any previous mappings for the process' pages
2348 // in the range from addr to addr + len." Thus, we will record a series of mappings here, one for the header
2349 // and each of the sections, as well as all the space between them that we give PROT_NONE protections.
2350
2351 // We're going to start adding mappings to the mapping list, so take the critical section
2352 InternalEnterCriticalSection(pThread, &mapping_critsec);
2353
2354#ifdef BIT64
2355 // First try to reserve virtual memory using ExecutableAllocator. This allows all PE images to be
2356 // near each other and close to the coreclr library which also allows the runtime to generate
2357 // more efficient code (by avoiding usage of jump stubs). Alignment to a 64 KB granularity should
2358 // not be necessary (alignment to page size should be sufficient), but see
2359 // ExecutableMemoryAllocator::AllocateMemory() for the reason why it is done.
2360 loadedBase = ReserveMemoryFromExecutableAllocator(pThread, ALIGN_UP(virtualSize, VIRTUAL_64KB));
2361#endif // BIT64
2362
2363 if (loadedBase == NULL)
2364 {
2365 // MAC64 requires we pass MAP_SHARED (or MAP_PRIVATE) flags - otherwise, the call is failed.
2366 // Refer to mmap documentation at http://www.manpagez.com/man/2/mmap/ for details.
2367 loadedBase = mmap(NULL, virtualSize, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
2368 }
2369
2370 if (MAP_FAILED == loadedBase)
2371 {
2372 ERROR_(LOADER)( "mmap failed with code %d: %s.\n", errno, strerror( errno ) );
2373 palError = FILEGetLastErrorFromErrno();
2374 loadedBase = NULL; // clear it so we don't try to use it during clean-up
2375 goto doneReleaseMappingCriticalSection;
2376 }
2377
2378 // All subsequent mappings of the PE file will be in the range [loadedBase, loadedBase + virtualSize)
2379
2380#if _DEBUG
2381 if (forceRelocs)
2382 {
2383 _ASSERTE(((SIZE_T)loadedBase) != preferredBase);
2384 munmap(pForceRelocBase, GetVirtualPageSize()); // now that we've forced relocation, let the original address mapping go
2385 }
2386 if (((SIZE_T)loadedBase) != preferredBase)
2387 {
2388 TRACE_(LOADER)("Image rebased from preferredBase of %p to loadedBase of %p\n", preferredBase, loadedBase);
2389 }
2390 else
2391 {
2392 TRACE_(LOADER)("Image loaded at preferred base %p\n", loadedBase);
2393 }
2394#endif // _DEBUG
2395
2396 //we have now reserved memory (potentially we got rebased). Walk the PE sections and map each part
2397 //separately.
2398
2399 size_t headerSize;
2400 headerSize = GetVirtualPageSize(); // if there are lots of sections, this could be wrong
2401
2402 //first, map the PE header to the first page in the image. Get pointers to the section headers
2403 palError = MAPmmapAndRecord(pFileObject, loadedBase,
2404 loadedBase, headerSize, PROT_READ, MAP_FILE|MAP_PRIVATE|MAP_FIXED, fd, 0,
2405 (void**)&loadedHeader);
2406 if (NO_ERROR != palError)
2407 {
2408 ERROR_(LOADER)( "mmap of PE header failed\n" );
2409 goto doneReleaseMappingCriticalSection;
2410 }
2411
2412 TRACE_(LOADER)("PE header loaded @ %p\n", loadedHeader);
2413 _ASSERTE(loadedHeader == loadedBase); // we already preallocated the space, and we used MAP_FIXED, so we should have gotten this address
2414 IMAGE_SECTION_HEADER * firstSection;
2415 firstSection = (IMAGE_SECTION_HEADER*)(((char *)loadedHeader)
2416 + loadedHeader->e_lfanew
2417 + offsetof(IMAGE_NT_HEADERS, OptionalHeader)
2418 + VAL16(ntHeader.FileHeader.SizeOfOptionalHeader));
2419 unsigned numSections;
2420 numSections = ntHeader.FileHeader.NumberOfSections;
2421
2422 // Validation
2423 char* sectionHeaderEnd;
2424 sectionHeaderEnd = (char*)firstSection + numSections * sizeof(IMAGE_SECTION_HEADER);
2425 if ( ((void*)firstSection < loadedBase)
2426 || ((char*)firstSection > sectionHeaderEnd)
2427 || (sectionHeaderEnd > (char*)loadedBase + virtualSize)
2428 )
2429 {
2430 ERROR_(LOADER)( "image is corrupt\n" );
2431 palError = ERROR_INVALID_PARAMETER;
2432 goto doneReleaseMappingCriticalSection;
2433 }
2434
2435 void* prevSectionEnd;
2436 prevSectionEnd = (char*)loadedBase + headerSize; // the first "section" for our purposes is the header
2437 for (unsigned i = 0; i < numSections; ++i)
2438 {
2439 //for each section, map the section of the file to the correct virtual offset. Gather the
2440 //protection bits from the PE file and convert them to the correct mmap PROT_* flags.
2441 void * sectionData;
2442 int prot = 0;
2443 IMAGE_SECTION_HEADER &currentHeader = firstSection[i];
2444
2445 void* sectionBase = (char*)loadedBase + currentHeader.VirtualAddress;
2446 void* sectionBaseAligned = ALIGN_DOWN(sectionBase, GetVirtualPageSize());
2447
2448 // Validate the section header
2449 if ( (sectionBase < loadedBase) // Did computing the section base overflow?
2450 || ((char*)sectionBase + currentHeader.SizeOfRawData < (char*)sectionBase) // Does the section overflow?
2451 || ((char*)sectionBase + currentHeader.SizeOfRawData > (char*)loadedBase + virtualSize) // Does the section extend past the end of the image as the header stated?
2452 || (prevSectionEnd > sectionBase) // Does this section overlap the previous one?
2453 )
2454 {
2455 ERROR_(LOADER)( "section %d is corrupt\n", i );
2456 palError = ERROR_INVALID_PARAMETER;
2457 goto doneReleaseMappingCriticalSection;
2458 }
2459 if (currentHeader.Misc.VirtualSize > currentHeader.SizeOfRawData)
2460 {
2461 ERROR_(LOADER)( "no support for zero-padded sections, section %d\n", i );
2462 palError = ERROR_INVALID_PARAMETER;
2463 goto doneReleaseMappingCriticalSection;
2464 }
2465
2466 // Is there space between the previous section and this one? If so, add a PROT_NONE mapping to cover it.
2467 if (prevSectionEnd < sectionBaseAligned)
2468 {
2469 palError = MAPRecordMapping(pFileObject,
2470 loadedBase,
2471 prevSectionEnd,
2472 (char*)sectionBaseAligned - (char*)prevSectionEnd,
2473 PROT_NONE);
2474 if (NO_ERROR != palError)
2475 {
2476 ERROR_(LOADER)( "recording gap section before section %d failed\n", i );
2477 goto doneReleaseMappingCriticalSection;
2478 }
2479 }
2480
2481 //Don't discard these sections. We need them to verify PE files
2482 //if (currentHeader.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
2483 // continue;
2484 if (currentHeader.Characteristics & IMAGE_SCN_MEM_EXECUTE)
2485 prot |= PROT_EXEC;
2486 if (currentHeader.Characteristics & IMAGE_SCN_MEM_READ)
2487 prot |= PROT_READ;
2488 if (currentHeader.Characteristics & IMAGE_SCN_MEM_WRITE)
2489 prot |= PROT_WRITE;
2490
2491 palError = MAPmmapAndRecord(pFileObject, loadedBase,
2492 sectionBase,
2493 currentHeader.SizeOfRawData,
2494 prot,
2495 MAP_FILE|MAP_PRIVATE|MAP_FIXED,
2496 fd,
2497 currentHeader.PointerToRawData,
2498 &sectionData);
2499 if (NO_ERROR != palError)
2500 {
2501 ERROR_(LOADER)( "mmap of section %d failed\n", i );
2502 goto doneReleaseMappingCriticalSection;
2503 }
2504
2505#if _DEBUG
2506 {
2507 // Ensure null termination of section name (which is allowed to not be null terminated if exactly 8 characters long)
2508 char sectionName[9];
2509 sectionName[8] = '\0';
2510 memcpy(sectionName, currentHeader.Name, sizeof(currentHeader.Name));
2511 TRACE_(LOADER)("Section %d '%s' (header @ %p) loaded @ %p (RVA: 0x%x, SizeOfRawData: 0x%x, PointerToRawData: 0x%x)\n",
2512 i, sectionName, &currentHeader, sectionData, currentHeader.VirtualAddress, currentHeader.SizeOfRawData, currentHeader.PointerToRawData);
2513 }
2514#endif // _DEBUG
2515
2516 prevSectionEnd = ALIGN_UP((char*)sectionBase + currentHeader.SizeOfRawData, GetVirtualPageSize()); // round up to page boundary
2517 }
2518
2519 // Is there space after the last section and before the end of the mapped image? If so, add a PROT_NONE mapping to cover it.
2520 char* imageEnd;
2521 imageEnd = (char*)loadedBase + virtualSize; // actually, points just after the mapped end
2522 if (prevSectionEnd < imageEnd)
2523 {
2524 palError = MAPRecordMapping(pFileObject,
2525 loadedBase,
2526 prevSectionEnd,
2527 (char*)imageEnd - (char*)prevSectionEnd,
2528 PROT_NONE);
2529 if (NO_ERROR != palError)
2530 {
2531 ERROR_(LOADER)( "recording end of image gap section failed\n" );
2532 goto doneReleaseMappingCriticalSection;
2533 }
2534 }
2535
2536 palError = ERROR_SUCCESS;
2537
2538doneReleaseMappingCriticalSection:
2539
2540 InternalLeaveCriticalSection(pThread, &mapping_critsec);
2541
2542done:
2543
2544 if (NULL != pLocalDataLock)
2545 {
2546 pLocalDataLock->ReleaseLock(pThread, FALSE);
2547 }
2548
2549 if (NULL != pFileObject)
2550 {
2551 pFileObject->ReleaseReference(pThread);
2552 }
2553
2554 if (palError == ERROR_SUCCESS)
2555 {
2556 retval = loadedBase;
2557 LOGEXIT("MAPMapPEFile returns %p\n", retval);
2558 }
2559 else
2560 {
2561 retval = NULL;
2562 LOGEXIT("MAPMapPEFile error: %d\n", palError);
2563
2564 // If we had an error, and had mapped anything, we need to unmap it
2565 if (loadedBase != NULL)
2566 {
2567 MAPUnmapPEFile(loadedBase);
2568 }
2569 }
2570 return retval;
2571}
2572
2573/*++
2574Function :
2575 MAPUnmapPEFile - unmap a PE file, and remove it from the recorded list of PE files mapped
2576
2577 returns TRUE if successful, FALSE otherwise
2578--*/
2579BOOL MAPUnmapPEFile(LPCVOID lpAddress)
2580{
2581 TRACE_(LOADER)("MAPUnmapPEFile(lpAddress=%p)\n", lpAddress);
2582
2583 if ( NULL == lpAddress )
2584 {
2585 ERROR_(LOADER)( "lpAddress cannot be NULL\n" );
2586 return FALSE;
2587 }
2588
2589 BOOL retval = TRUE;
2590 CPalThread * pThread = InternalGetCurrentThread();
2591 InternalEnterCriticalSection(pThread, &mapping_critsec);
2592 PLIST_ENTRY pLink, pLinkNext, pLinkLocal = NULL;
2593 unsigned nPESections = 0;
2594
2595 // Look through the entire MappedViewList for all mappings associated with the
2596 // PE file with base address 'lpAddress'. We want to unmap all the memory
2597 // and then release the file mapping object. Unfortunately, based on the comment
2598 // in CorUnix::InternalUnmapViewOfFile(), we can't release the file mapping object
2599 // while within the mapping critical section. So, we unlink all the elements from the
2600 // main list while in the critical section, and then run through this local list
2601 // doing the real work after releasing the main list critical section. The order
2602 // of the unmapping doesn't matter, so we don't fully set all the list link pointers,
2603 // only a minimal set.
2604
2605 for(pLink = MappedViewList.Flink;
2606 pLink != &MappedViewList;
2607 pLink = pLinkNext)
2608 {
2609 pLinkNext = pLink->Flink;
2610 PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
2611
2612 if (pView->lpPEBaseAddress == lpAddress) // this entry is associated with the PE file
2613 {
2614 ++nPESections; // for debugging, check that we see at least one
2615
2616 RemoveEntryList(&pView->Link);
2617 pView->Link.Flink = pLinkLocal; // the local list is singly-linked, NULL terminated
2618 pLinkLocal = &pView->Link;
2619 }
2620 }
2621
2622#if _DEBUG
2623 if (nPESections == 0)
2624 {
2625 ERROR_(LOADER)( "MAPUnmapPEFile called to unmap a file that was not in the PE file mapping list\n" );
2626 }
2627#endif // _DEBUG
2628
2629 InternalLeaveCriticalSection(pThread, &mapping_critsec);
2630
2631 // Now, outside the critical section, do the actual unmapping work
2632
2633 for(pLink = pLinkLocal;
2634 pLink != NULL;
2635 pLink = pLinkNext)
2636 {
2637 pLinkNext = pLink->Flink;
2638 PMAPPED_VIEW_LIST pView = CONTAINING_RECORD(pLink, MAPPED_VIEW_LIST, Link);
2639
2640 // remove pView mapping from the list
2641 if (-1 == munmap(pView->lpAddress, pView->NumberOfBytesToMap))
2642 {
2643 // Emit an error message in a trace, but continue trying to do the rest
2644 ERROR_(LOADER)("Unable to unmap the file. Expect trouble.\n");
2645 retval = FALSE;
2646 }
2647
2648 IPalObject* pFileObject = pView->pFileMapping;
2649 if (NULL != pFileObject)
2650 {
2651 pFileObject->ReleaseReference(pThread);
2652 }
2653 free(pView); // this leaves pLink dangling
2654 }
2655
2656 TRACE_(LOADER)("MAPUnmapPEFile returning %d\n", retval);
2657 return retval;
2658}
2659