MSVC compatibility fixes.
[wine] / memory / virtual.c
1 /*
2  * Win32 virtual memory functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #ifdef HAVE_SYS_ERRNO_H
27 #include <sys/errno.h>
28 #endif
29 #include <fcntl.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_MMAN_H
38 #include <sys/mman.h>
39 #endif
40 #include "winnls.h"
41 #include "winbase.h"
42 #include "wine/exception.h"
43 #include "wine/unicode.h"
44 #include "wine/library.h"
45 #include "winerror.h"
46 #include "file.h"
47 #include "global.h"
48 #include "wine/server.h"
49 #include "msvcrt/excpt.h"
50 #include "wine/debug.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(virtual);
53 WINE_DECLARE_DEBUG_CHANNEL(module);
54
55 #ifndef MS_SYNC
56 #define MS_SYNC 0
57 #endif
58
59 /* File view */
60 typedef struct _FV
61 {
62     struct _FV   *next;        /* Next view */
63     struct _FV   *prev;        /* Prev view */
64     void         *base;        /* Base address */
65     UINT          size;        /* Size in bytes */
66     UINT          flags;       /* Allocation flags */
67     HANDLE        mapping;     /* Handle to the file mapping */
68     HANDLERPROC   handlerProc; /* Fault handler */
69     LPVOID        handlerArg;  /* Fault handler argument */
70     BYTE          protect;     /* Protection for all pages at allocation time */
71     BYTE          prot[1];     /* Protection byte for each page */
72 } FILE_VIEW;
73
74 /* Per-view flags */
75 #define VFLAG_SYSTEM     0x01
76 #define VFLAG_VALLOC     0x02  /* allocated by VirtualAlloc */
77
78 /* Conversion from VPROT_* to Win32 flags */
79 static const BYTE VIRTUAL_Win32Flags[16] =
80 {
81     PAGE_NOACCESS,              /* 0 */
82     PAGE_READONLY,              /* READ */
83     PAGE_READWRITE,             /* WRITE */
84     PAGE_READWRITE,             /* READ | WRITE */
85     PAGE_EXECUTE,               /* EXEC */
86     PAGE_EXECUTE_READ,          /* READ | EXEC */
87     PAGE_EXECUTE_READWRITE,     /* WRITE | EXEC */
88     PAGE_EXECUTE_READWRITE,     /* READ | WRITE | EXEC */
89     PAGE_WRITECOPY,             /* WRITECOPY */
90     PAGE_WRITECOPY,             /* READ | WRITECOPY */
91     PAGE_WRITECOPY,             /* WRITE | WRITECOPY */
92     PAGE_WRITECOPY,             /* READ | WRITE | WRITECOPY */
93     PAGE_EXECUTE_WRITECOPY,     /* EXEC | WRITECOPY */
94     PAGE_EXECUTE_WRITECOPY,     /* READ | EXEC | WRITECOPY */
95     PAGE_EXECUTE_WRITECOPY,     /* WRITE | EXEC | WRITECOPY */
96     PAGE_EXECUTE_WRITECOPY      /* READ | WRITE | EXEC | WRITECOPY */
97 };
98
99
100 static FILE_VIEW *VIRTUAL_FirstView;
101 static CRITICAL_SECTION csVirtual = CRITICAL_SECTION_INIT("csVirtual");
102
103 #ifdef __i386__
104 /* These are always the same on an i386, and it will be faster this way */
105 # define page_mask  0xfff
106 # define page_shift 12
107 # define page_size  0x1000
108 #else
109 static UINT page_shift;
110 static UINT page_mask;
111 static UINT page_size;
112 #endif  /* __i386__ */
113 #define granularity_mask 0xffff  /* Allocation granularity (usually 64k) */
114
115 #define ADDRESS_SPACE_LIMIT  ((void *)0xc0000000)  /* top of the user address space */
116
117 #define ROUND_ADDR(addr,mask) \
118    ((void *)((UINT_PTR)(addr) & ~(mask)))
119
120 #define ROUND_SIZE(addr,size) \
121    (((UINT)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
122
123 #define VIRTUAL_DEBUG_DUMP_VIEW(view) \
124    if (!TRACE_ON(virtual)); else VIRTUAL_DumpView(view)
125
126 static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size, DWORD offset_low,
127                             DWORD offset_high, int prot, int flags, BOOL *removable );
128
129 /* filter for page-fault exceptions */
130 static WINE_EXCEPTION_FILTER(page_fault)
131 {
132     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
133         return EXCEPTION_EXECUTE_HANDLER;
134     return EXCEPTION_CONTINUE_SEARCH;
135 }
136
137 /***********************************************************************
138  *           VIRTUAL_GetProtStr
139  */
140 static const char *VIRTUAL_GetProtStr( BYTE prot )
141 {
142     static char buffer[6];
143     buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-';
144     buffer[1] = (prot & VPROT_GUARD) ? 'g' : '-';
145     buffer[2] = (prot & VPROT_READ) ? 'r' : '-';
146     buffer[3] = (prot & VPROT_WRITE) ?
147                     ((prot & VPROT_WRITECOPY) ? 'w' : 'W') : '-';
148     buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-';
149     buffer[5] = 0;
150     return buffer;
151 }
152
153
154 /***********************************************************************
155  *           VIRTUAL_DumpView
156  */
157 static void VIRTUAL_DumpView( FILE_VIEW *view )
158 {
159     UINT i, count;
160     char *addr = view->base;
161     BYTE prot = view->prot[0];
162
163     DPRINTF( "View: %p - %p", addr, addr + view->size - 1 );
164     if (view->flags & VFLAG_SYSTEM)
165         DPRINTF( " (system)\n" );
166     else if (view->flags & VFLAG_VALLOC)
167         DPRINTF( " (valloc)\n" );
168     else if (view->mapping)
169         DPRINTF( " %d\n", view->mapping );
170     else
171         DPRINTF( " (anonymous)\n");
172
173     for (count = i = 1; i < view->size >> page_shift; i++, count++)
174     {
175         if (view->prot[i] == prot) continue;
176         DPRINTF( "      %p - %p %s\n",
177                  addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
178         addr += (count << page_shift);
179         prot = view->prot[i];
180         count = 0;
181     }
182     if (count)
183         DPRINTF( "      %p - %p %s\n",
184                  addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
185 }
186
187
188 /***********************************************************************
189  *           VIRTUAL_Dump
190  */
191 void VIRTUAL_Dump(void)
192 {
193     FILE_VIEW *view;
194     DPRINTF( "\nDump of all virtual memory views:\n\n" );
195     EnterCriticalSection(&csVirtual);
196     view = VIRTUAL_FirstView;
197     while (view)
198     {
199         VIRTUAL_DumpView( view );
200         view = view->next;
201     }
202     LeaveCriticalSection(&csVirtual);
203 }
204
205
206 /***********************************************************************
207  *           VIRTUAL_FindView
208  *
209  * Find the view containing a given address.
210  *
211  * RETURNS
212  *      View: Success
213  *      NULL: Failure
214  */
215 static FILE_VIEW *VIRTUAL_FindView( const void *addr ) /* [in] Address */
216 {
217     FILE_VIEW *view;
218
219     EnterCriticalSection(&csVirtual);
220     view = VIRTUAL_FirstView;
221     while (view)
222     {
223         if (view->base > addr)
224         {
225             view = NULL;
226             break;
227         }
228         if ((char*)view->base + view->size > (char*)addr) break;
229         view = view->next;
230     }
231     LeaveCriticalSection(&csVirtual);
232     return view;
233 }
234
235
236 /***********************************************************************
237  *           VIRTUAL_CreateView
238  *
239  * Create a new view and add it in the linked list.
240  */
241 static FILE_VIEW *VIRTUAL_CreateView( void *base, UINT size, UINT flags,
242                                       BYTE vprot, HANDLE mapping )
243 {
244     FILE_VIEW *view, *prev;
245
246     /* Create the view structure */
247
248     assert( !((unsigned int)base & page_mask) );
249     assert( !(size & page_mask) );
250     size >>= page_shift;
251     if (!(view = (FILE_VIEW *)malloc( sizeof(*view) + size - 1 ))) return NULL;
252     view->base    = base;
253     view->size    = size << page_shift;
254     view->flags   = flags;
255     view->mapping = mapping;
256     view->protect = vprot;
257     view->handlerProc = NULL;
258     memset( view->prot, vprot, size );
259
260     /* Duplicate the mapping handle */
261
262     if (view->mapping &&
263         !DuplicateHandle( GetCurrentProcess(), view->mapping,
264                           GetCurrentProcess(), &view->mapping,
265                           0, FALSE, DUPLICATE_SAME_ACCESS ))
266     {
267         free( view );
268         return NULL;
269     }
270
271     /* Insert it in the linked list */
272
273     EnterCriticalSection(&csVirtual);
274     if (!VIRTUAL_FirstView || (VIRTUAL_FirstView->base > base))
275     {
276         view->next = VIRTUAL_FirstView;
277         view->prev = NULL;
278         if (view->next) view->next->prev = view;
279         VIRTUAL_FirstView = view;
280     }
281     else
282     {
283         prev = VIRTUAL_FirstView;
284         while (prev->next && (prev->next->base < base)) prev = prev->next;
285         view->next = prev->next;
286         view->prev = prev;
287         if (view->next) view->next->prev = view;
288         prev->next  = view;
289     }
290     LeaveCriticalSection(&csVirtual);
291     VIRTUAL_DEBUG_DUMP_VIEW( view );
292     return view;
293 }
294
295
296 /***********************************************************************
297  *           VIRTUAL_DeleteView
298  * Deletes a view.
299  *
300  * RETURNS
301  *      None
302  */
303 static void VIRTUAL_DeleteView(
304             FILE_VIEW *view /* [in] View */
305 ) {
306     if (!(view->flags & VFLAG_SYSTEM))
307         munmap( (void *)view->base, view->size );
308     EnterCriticalSection(&csVirtual);
309     if (view->next) view->next->prev = view->prev;
310     if (view->prev) view->prev->next = view->next;
311     else VIRTUAL_FirstView = view->next;
312     LeaveCriticalSection(&csVirtual);
313     if (view->mapping) NtClose( view->mapping );
314     free( view );
315 }
316
317
318 /***********************************************************************
319  *           VIRTUAL_GetUnixProt
320  *
321  * Convert page protections to protection for mmap/mprotect.
322  */
323 static int VIRTUAL_GetUnixProt( BYTE vprot )
324 {
325     int prot = 0;
326     if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD))
327     {
328         if (vprot & VPROT_READ) prot |= PROT_READ;
329         if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
330         if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE;
331         if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
332     }
333     return prot;
334 }
335
336
337 /***********************************************************************
338  *           VIRTUAL_GetWin32Prot
339  *
340  * Convert page protections to Win32 flags.
341  *
342  * RETURNS
343  *      None
344  */
345 static void VIRTUAL_GetWin32Prot(
346             BYTE vprot,     /* [in] Page protection flags */
347             DWORD *protect, /* [out] Location to store Win32 protection flags */
348             DWORD *state    /* [out] Location to store mem state flag */
349 ) {
350     if (protect) {
351         *protect = VIRTUAL_Win32Flags[vprot & 0x0f];
352 /*      if (vprot & VPROT_GUARD) *protect |= PAGE_GUARD;*/
353         if (vprot & VPROT_NOCACHE) *protect |= PAGE_NOCACHE;
354
355         if (vprot & VPROT_GUARD) *protect = PAGE_NOACCESS;
356     }
357
358     if (state) *state = (vprot & VPROT_COMMITTED) ? MEM_COMMIT : MEM_RESERVE;
359 }
360
361
362 /***********************************************************************
363  *           VIRTUAL_GetProt
364  *
365  * Build page protections from Win32 flags.
366  *
367  * RETURNS
368  *      Value of page protection flags
369  */
370 static BYTE VIRTUAL_GetProt(
371             DWORD protect  /* [in] Win32 protection flags */
372 ) {
373     BYTE vprot;
374
375     switch(protect & 0xff)
376     {
377     case PAGE_READONLY:
378         vprot = VPROT_READ;
379         break;
380     case PAGE_READWRITE:
381         vprot = VPROT_READ | VPROT_WRITE;
382         break;
383     case PAGE_WRITECOPY:
384         /* MSDN CreateFileMapping() states that if PAGE_WRITECOPY is given,
385          * that the hFile must have been opened with GENERIC_READ and
386          * GENERIC_WRITE access.  This is WRONG as tests show that you
387          * only need GENERIC_READ access (at least for Win9x,
388          * FIXME: what about NT?).  Thus, we don't put VPROT_WRITE in
389          * PAGE_WRITECOPY and PAGE_EXECUTE_WRITECOPY.
390          */
391         vprot = VPROT_READ | VPROT_WRITECOPY;
392         break;
393     case PAGE_EXECUTE:
394         vprot = VPROT_EXEC;
395         break;
396     case PAGE_EXECUTE_READ:
397         vprot = VPROT_EXEC | VPROT_READ;
398         break;
399     case PAGE_EXECUTE_READWRITE:
400         vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE;
401         break;
402     case PAGE_EXECUTE_WRITECOPY:
403         /* See comment for PAGE_WRITECOPY above */
404         vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITECOPY;
405         break;
406     case PAGE_NOACCESS:
407     default:
408         vprot = 0;
409         break;
410     }
411     if (protect & PAGE_GUARD) vprot |= VPROT_GUARD;
412     if (protect & PAGE_NOCACHE) vprot |= VPROT_NOCACHE;
413     return vprot;
414 }
415
416
417 /***********************************************************************
418  *           VIRTUAL_SetProt
419  *
420  * Change the protection of a range of pages.
421  *
422  * RETURNS
423  *      TRUE: Success
424  *      FALSE: Failure
425  */
426 static BOOL VIRTUAL_SetProt( FILE_VIEW *view, /* [in] Pointer to view */
427                              void *base,      /* [in] Starting address */
428                              UINT size,       /* [in] Size in bytes */
429                              BYTE vprot )     /* [in] Protections to use */
430 {
431     TRACE("%p-%p %s\n",
432           base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) );
433
434     if (mprotect( base, size, VIRTUAL_GetUnixProt(vprot) ))
435         return FALSE;  /* FIXME: last error */
436
437     memset( view->prot + (((char *)base - (char *)view->base) >> page_shift),
438             vprot, size >> page_shift );
439     VIRTUAL_DEBUG_DUMP_VIEW( view );
440     return TRUE;
441 }
442
443
444 /***********************************************************************
445  *           anon_mmap_aligned
446  *
447  * Create an anonymous mapping aligned to the allocation granularity.
448  */
449 static void *anon_mmap_aligned( void *base, unsigned int size, int prot, int flags )
450 {
451     void *ptr;
452     unsigned int view_size = size + (base ? 0 : granularity_mask + 1);
453
454     if ((ptr = wine_anon_mmap( base, view_size, prot, flags )) == (void *)-1)
455     {
456         /* KB: Q125713, 25-SEP-1995, "Common File Mapping Problems and
457          * Platform Differences":
458          * Windows NT: ERROR_INVALID_PARAMETER
459          * Windows 95: ERROR_INVALID_ADDRESS.
460          */
461         if (errno == ENOMEM) SetLastError( ERROR_OUTOFMEMORY );
462         else
463         {
464             if (GetVersion() & 0x80000000)  /* win95 */
465                 SetLastError( ERROR_INVALID_ADDRESS );
466             else
467                 SetLastError( ERROR_INVALID_PARAMETER );
468         }
469         return ptr;
470     }
471
472     if (!base)
473     {
474         /* Release the extra memory while keeping the range
475          * starting on the granularity boundary. */
476         if ((unsigned int)ptr & granularity_mask)
477         {
478             unsigned int extra = granularity_mask + 1 - ((unsigned int)ptr & granularity_mask);
479             munmap( ptr, extra );
480             ptr = (char *)ptr + extra;
481             view_size -= extra;
482         }
483         if (view_size > size)
484             munmap( (char *)ptr + size, view_size - size );
485     }
486     else if (ptr != base)
487     {
488         /* We couldn't get the address we wanted */
489         munmap( ptr, view_size );
490         SetLastError( ERROR_INVALID_ADDRESS );
491         ptr = (void *)-1;
492     }
493     return ptr;
494 }
495
496
497 /***********************************************************************
498  *           do_relocations
499  *
500  * Apply the relocations to a mapped PE image
501  */
502 static int do_relocations( char *base, const IMAGE_DATA_DIRECTORY *dir,
503                            int delta, DWORD total_size )
504 {
505     IMAGE_BASE_RELOCATION *rel;
506
507     TRACE_(module)( "relocating from %p-%p to %p-%p\n",
508                     base - delta, base - delta + total_size, base, base + total_size );
509
510     for (rel = (IMAGE_BASE_RELOCATION *)(base + dir->VirtualAddress);
511          ((char *)rel < base + dir->VirtualAddress + dir->Size) && rel->SizeOfBlock;
512          rel = (IMAGE_BASE_RELOCATION*)((char*)rel + rel->SizeOfBlock) )
513     {
514         char *page = base + rel->VirtualAddress;
515         WORD *TypeOffset = (WORD *)(rel + 1);
516         int i, count = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(*TypeOffset);
517
518         if (!count) continue;
519
520         /* sanity checks */
521         if ((char *)rel + rel->SizeOfBlock > base + dir->VirtualAddress + dir->Size ||
522             page > base + total_size)
523         {
524             ERR_(module)("invalid relocation %p,%lx,%ld at %p,%lx,%lx\n",
525                          rel, rel->VirtualAddress, rel->SizeOfBlock,
526                          base, dir->VirtualAddress, dir->Size );
527             return 0;
528         }
529
530         TRACE_(module)("%ld relocations for page %lx\n", rel->SizeOfBlock, rel->VirtualAddress);
531
532         /* patching in reverse order */
533         for (i = 0 ; i < count; i++)
534         {
535             int offset = TypeOffset[i] & 0xFFF;
536             int type = TypeOffset[i] >> 12;
537             switch(type)
538             {
539             case IMAGE_REL_BASED_ABSOLUTE:
540                 break;
541             case IMAGE_REL_BASED_HIGH:
542                 *(short*)(page+offset) += HIWORD(delta);
543                 break;
544             case IMAGE_REL_BASED_LOW:
545                 *(short*)(page+offset) += LOWORD(delta);
546                 break;
547             case IMAGE_REL_BASED_HIGHLOW:
548                 *(int*)(page+offset) += delta;
549                 /* FIXME: if this is an exported address, fire up enhanced logic */
550                 break;
551             default:
552                 FIXME_(module)("Unknown/unsupported fixup type %d.\n", type);
553                 break;
554             }
555         }
556     }
557     return 1;
558 }
559
560
561 /***********************************************************************
562  *           map_image
563  *
564  * Map an executable (PE format) image into memory.
565  */
566 static LPVOID map_image( HANDLE hmapping, int fd, char *base, DWORD total_size,
567                          DWORD header_size, HANDLE shared_file, DWORD shared_size,
568                          BOOL removable )
569 {
570     IMAGE_DOS_HEADER *dos;
571     IMAGE_NT_HEADERS *nt;
572     IMAGE_SECTION_HEADER *sec;
573     IMAGE_DATA_DIRECTORY *imports;
574     int i, pos;
575     DWORD err = GetLastError();
576     FILE_VIEW *view;
577     char *ptr;
578     int shared_fd = -1;
579
580     SetLastError( ERROR_BAD_EXE_FORMAT );  /* generic error */
581
582     /* zero-map the whole range */
583
584     if (base < (char *)0x110000 ||  /* make sure the DOS area remains free */
585         (ptr = wine_anon_mmap( base, total_size,
586                                PROT_READ | PROT_WRITE | PROT_EXEC, 0 )) == (char *)-1)
587     {
588         ptr = wine_anon_mmap( NULL, total_size,
589                             PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
590         if (ptr == (char *)-1)
591         {
592             ERR_(module)("Not enough memory for module (%ld bytes)\n", total_size);
593             goto error;
594         }
595     }
596     TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );
597
598     /* map the header */
599
600     if (VIRTUAL_mmap( fd, ptr, header_size, 0, 0, PROT_READ,
601                       MAP_PRIVATE | MAP_FIXED, &removable ) == (char *)-1) goto error;
602     dos = (IMAGE_DOS_HEADER *)ptr;
603     nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
604     if ((char *)(nt + 1) > ptr + header_size) goto error;
605
606     sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
607     if ((char *)(sec + nt->FileHeader.NumberOfSections) > ptr + header_size) goto error;
608
609     imports = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
610     if (!imports->Size || !imports->VirtualAddress) imports = NULL;
611
612     /* check the architecture */
613
614     if (nt->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
615     {
616         MESSAGE("Trying to load PE image for unsupported architecture (");
617         switch (nt->FileHeader.Machine)
618         {
619         case IMAGE_FILE_MACHINE_UNKNOWN: MESSAGE("Unknown"); break;
620         case IMAGE_FILE_MACHINE_I860:    MESSAGE("I860"); break;
621         case IMAGE_FILE_MACHINE_R3000:   MESSAGE("R3000"); break;
622         case IMAGE_FILE_MACHINE_R4000:   MESSAGE("R4000"); break;
623         case IMAGE_FILE_MACHINE_R10000:  MESSAGE("R10000"); break;
624         case IMAGE_FILE_MACHINE_ALPHA:   MESSAGE("Alpha"); break;
625         case IMAGE_FILE_MACHINE_POWERPC: MESSAGE("PowerPC"); break;
626         default: MESSAGE("Unknown-%04x", nt->FileHeader.Machine); break;
627         }
628         MESSAGE(")\n");
629         goto error;
630     }
631
632     /* retrieve the shared sections file */
633
634     if (shared_size)
635     {
636         if ((shared_fd = FILE_GetUnixHandle( shared_file, GENERIC_READ )) == -1) goto error;
637         CloseHandle( shared_file );  /* we no longer need it */
638         shared_file = 0;
639     }
640
641     /* map all the sections */
642
643     for (i = pos = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
644     {
645         DWORD size;
646
647         /* a few sanity checks */
648         size = sec->VirtualAddress + ROUND_SIZE( sec->VirtualAddress, sec->Misc.VirtualSize );
649         if (sec->VirtualAddress > total_size || size > total_size || size < sec->VirtualAddress)
650         {
651             ERR_(module)( "Section %.8s too large (%lx+%lx/%lx)\n",
652                           sec->Name, sec->VirtualAddress, sec->Misc.VirtualSize, total_size );
653             goto error;
654         }
655
656         if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) &&
657             (sec->Characteristics & IMAGE_SCN_MEM_WRITE))
658         {
659             size = ROUND_SIZE( 0, sec->Misc.VirtualSize );
660             TRACE_(module)( "mapping shared section %.8s at %p off %lx (%x) size %lx (%lx) flags %lx\n",
661                           sec->Name, ptr + sec->VirtualAddress,
662                           sec->PointerToRawData, pos, sec->SizeOfRawData,
663                           size, sec->Characteristics );
664             if (VIRTUAL_mmap( shared_fd, ptr + sec->VirtualAddress, size,
665                               pos, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
666                               MAP_SHARED|MAP_FIXED, NULL ) == (void *)-1)
667             {
668                 ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
669                 goto error;
670             }
671
672             /* check if the import directory falls inside this section */
673             if (imports && imports->VirtualAddress >= sec->VirtualAddress &&
674                 imports->VirtualAddress < sec->VirtualAddress + size)
675             {
676                 DWORD base = imports->VirtualAddress & ~page_mask;
677                 DWORD end = imports->VirtualAddress + ROUND_SIZE( imports->VirtualAddress,
678                                                                   imports->Size );
679                 if (end > sec->VirtualAddress + size) end = sec->VirtualAddress + size;
680                 if (end > base) VIRTUAL_mmap( shared_fd, ptr + base, end - base,
681                                               pos, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
682                                               MAP_PRIVATE|MAP_FIXED, NULL );
683             }
684             pos += size;
685             continue;
686         }
687
688         TRACE_(module)( "mapping section %.8s at %p off %lx size %lx flags %lx\n",
689                         sec->Name, ptr + sec->VirtualAddress,
690                         sec->PointerToRawData, sec->SizeOfRawData,
691                         sec->Characteristics );
692
693         if (sec->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) continue;
694         if (!sec->PointerToRawData || !sec->SizeOfRawData) continue;
695
696         /* Note: if the section is not aligned properly VIRTUAL_mmap will magically
697          *       fall back to read(), so we don't need to check anything here.
698          */
699         if (VIRTUAL_mmap( fd, ptr + sec->VirtualAddress, sec->SizeOfRawData,
700                           sec->PointerToRawData, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
701                           MAP_PRIVATE | MAP_FIXED, &removable ) == (void *)-1)
702         {
703             ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
704             goto error;
705         }
706
707         if ((sec->SizeOfRawData < sec->Misc.VirtualSize) && (sec->SizeOfRawData & page_mask))
708         {
709             DWORD end = ROUND_SIZE( 0, sec->SizeOfRawData );
710             if (end > sec->Misc.VirtualSize) end = sec->Misc.VirtualSize;
711             TRACE_(module)("clearing %p - %p\n",
712                            ptr + sec->VirtualAddress + sec->SizeOfRawData,
713                            ptr + sec->VirtualAddress + end );
714             memset( ptr + sec->VirtualAddress + sec->SizeOfRawData, 0,
715                     end - sec->SizeOfRawData );
716         }
717     }
718
719
720     /* perform base relocation, if necessary */
721
722     if (ptr != base)
723     {
724         const IMAGE_DATA_DIRECTORY *relocs;
725
726         relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
727         if (!relocs->VirtualAddress || !relocs->Size)
728         {
729             if (nt->OptionalHeader.ImageBase == 0x400000)
730                 ERR("Standard load address for a Win32 program (0x00400000) not available - security-patched kernel ?\n");
731             else
732                 ERR( "FATAL: Need to relocate module from addr %lx, but there are no relocation records\n",
733                      nt->OptionalHeader.ImageBase );
734             SetLastError( ERROR_BAD_EXE_FORMAT );
735             goto error;
736         }
737
738         /* FIXME: If we need to relocate a system DLL (base > 2GB) we should
739          *        really make sure that the *new* base address is also > 2GB.
740          *        Some DLLs really check the MSB of the module handle :-/
741          */
742         if ((nt->OptionalHeader.ImageBase & 0x80000000) && !((DWORD)base & 0x80000000))
743             ERR( "Forced to relocate system DLL (base > 2GB). This is not good.\n" );
744
745         if (!do_relocations( ptr, relocs, ptr - base, total_size ))
746         {
747             SetLastError( ERROR_BAD_EXE_FORMAT );
748             goto error;
749         }
750     }
751
752     if (removable) hmapping = 0;  /* don't keep handle open on removable media */
753     if (!(view = VIRTUAL_CreateView( ptr, total_size, 0, VPROT_COMMITTED|VPROT_READ, hmapping )))
754     {
755         SetLastError( ERROR_OUTOFMEMORY );
756         goto error;
757     }
758
759     /* set the image protections */
760
761     sec = (IMAGE_SECTION_HEADER*)((char *)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
762     for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
763     {
764         DWORD size = ROUND_SIZE( sec->VirtualAddress, sec->Misc.VirtualSize );
765         BYTE vprot = VPROT_COMMITTED;
766         if (sec->Characteristics & IMAGE_SCN_MEM_READ)    vprot |= VPROT_READ;
767         if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)   vprot |= VPROT_WRITE|VPROT_WRITECOPY;
768         if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) vprot |= VPROT_EXEC;
769
770         /* make sure the import directory is writable */
771         if (imports && imports->VirtualAddress >= sec->VirtualAddress &&
772             imports->VirtualAddress < sec->VirtualAddress + size)
773             vprot |= VPROT_READ|VPROT_WRITE|VPROT_WRITECOPY;
774
775         VIRTUAL_SetProt( view, ptr + sec->VirtualAddress, size, vprot );
776     }
777
778     SetLastError( err );  /* restore last error */
779     close( fd );
780     if (shared_fd != -1) close( shared_fd );
781     return ptr;
782
783  error:
784     if (ptr != (char *)-1) munmap( ptr, total_size );
785     close( fd );
786     if (shared_fd != -1) close( shared_fd );
787     if (shared_file) CloseHandle( shared_file );
788     return NULL;
789 }
790
791
792 /***********************************************************************
793  *           VIRTUAL_Init
794  */
795 #ifndef page_mask
796 DECL_GLOBAL_CONSTRUCTOR(VIRTUAL_Init)
797 {
798     page_size = getpagesize();
799     page_mask = page_size - 1;
800     /* Make sure we have a power of 2 */
801     assert( !(page_size & page_mask) );
802     page_shift = 0;
803     while ((1 << page_shift) != page_size) page_shift++;
804 }
805 #endif  /* page_mask */
806
807
808 /***********************************************************************
809  *           VIRTUAL_SetFaultHandler
810  */
811 BOOL VIRTUAL_SetFaultHandler( LPCVOID addr, HANDLERPROC proc, LPVOID arg )
812 {
813     FILE_VIEW *view;
814
815     if (!(view = VIRTUAL_FindView( addr ))) return FALSE;
816     view->handlerProc = proc;
817     view->handlerArg  = arg;
818     return TRUE;
819 }
820
821 /***********************************************************************
822  *           VIRTUAL_HandleFault
823  */
824 DWORD VIRTUAL_HandleFault( LPCVOID addr )
825 {
826     FILE_VIEW *view = VIRTUAL_FindView( addr );
827     DWORD ret = EXCEPTION_ACCESS_VIOLATION;
828
829     if (view)
830     {
831         if (view->handlerProc)
832         {
833             if (view->handlerProc(view->handlerArg, addr)) ret = 0;  /* handled */
834         }
835         else
836         {
837             BYTE vprot = view->prot[((char *)addr - (char *)view->base) >> page_shift];
838             void *page = (void *)((UINT_PTR)addr & ~page_mask);
839             char *stack = (char *)NtCurrentTeb()->stack_base + SIGNAL_STACK_SIZE + page_mask + 1;
840             if (vprot & VPROT_GUARD)
841             {
842                 VIRTUAL_SetProt( view, page, page_mask + 1, vprot & ~VPROT_GUARD );
843                 ret = STATUS_GUARD_PAGE_VIOLATION;
844             }
845             /* is it inside the stack guard pages? */
846             if (((char *)addr >= stack) && ((char *)addr < stack + 2*(page_mask+1)))
847                 ret = STATUS_STACK_OVERFLOW;
848         }
849     }
850     return ret;
851 }
852
853
854
855 /***********************************************************************
856  *           unaligned_mmap
857  *
858  * Linux kernels before 2.4.x can support non page-aligned offsets, as
859  * long as the offset is aligned to the filesystem block size. This is
860  * a big performance gain so we want to take advantage of it.
861  *
862  * However, when we use 64-bit file support this doesn't work because
863  * glibc rejects unaligned offsets. Also glibc 2.1.3 mmap64 is broken
864  * in that it rounds unaligned offsets down to a page boundary. For
865  * these reasons we do a direct system call here.
866  */
867 static void *unaligned_mmap( void *addr, size_t length, unsigned int prot,
868                              unsigned int flags, int fd, unsigned int offset_low,
869                              unsigned int offset_high )
870 {
871 #if defined(linux) && defined(__i386__) && defined(__GNUC__)
872     if (!offset_high && (offset_low & page_mask))
873     {
874         int ret;
875
876         struct
877         {
878             void        *addr;
879             unsigned int length;
880             unsigned int prot;
881             unsigned int flags;
882             unsigned int fd;
883             unsigned int offset;
884         } args;
885
886         args.addr   = addr;
887         args.length = length;
888         args.prot   = prot;
889         args.flags  = flags;
890         args.fd     = fd;
891         args.offset = offset_low;
892
893         __asm__ __volatile__("push %%ebx\n\t"
894                              "movl %2,%%ebx\n\t"
895                              "int $0x80\n\t"
896                              "popl %%ebx"
897                              : "=a" (ret)
898                              : "0" (90), /* SYS_mmap */
899                                "g" (&args) );
900         if (ret < 0 && ret > -4096)
901         {
902             errno = -ret;
903             ret = -1;
904         }
905         return (void *)ret;
906     }
907 #endif
908     return mmap( addr, length, prot, flags, fd, ((off_t)offset_high << 32) | offset_low );
909 }
910
911
912 /***********************************************************************
913  *           VIRTUAL_mmap
914  *
915  * Wrapper for mmap() that handles anonymous mappings portably,
916  * and falls back to read if mmap of a file fails.
917  */
918 static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size,
919                             DWORD offset_low, DWORD offset_high,
920                             int prot, int flags, BOOL *removable )
921 {
922     int pos;
923     LPVOID ret;
924     off_t offset;
925     BOOL is_shared_write = FALSE;
926
927     if (fd == -1) return wine_anon_mmap( start, size, prot, flags );
928
929     if (prot & PROT_WRITE)
930     {
931 #ifdef MAP_SHARED
932         if (flags & MAP_SHARED) is_shared_write = TRUE;
933 #endif
934 #ifdef MAP_PRIVATE
935         if (!(flags & MAP_PRIVATE)) is_shared_write = TRUE;
936 #endif
937     }
938
939     if (removable && *removable)
940     {
941         /* if on removable media, try using read instead of mmap */
942         if (!is_shared_write) goto fake_mmap;
943         *removable = FALSE;
944     }
945
946     if ((ret = unaligned_mmap( start, size, prot, flags, fd,
947                                offset_low, offset_high )) != (LPVOID)-1) return ret;
948
949     /* mmap() failed; if this is because the file offset is not    */
950     /* page-aligned (EINVAL), or because the underlying filesystem */
951     /* does not support mmap() (ENOEXEC,ENODEV), we do it by hand. */
952
953     if ((errno != ENOEXEC) && (errno != EINVAL) && (errno != ENODEV)) return ret;
954     if (is_shared_write) return ret;  /* we cannot fake shared write mappings */
955
956  fake_mmap:
957     /* Reserve the memory with an anonymous mmap */
958     ret = wine_anon_mmap( start, size, PROT_READ | PROT_WRITE, flags );
959     if (ret == (LPVOID)-1) return ret;
960     /* Now read in the file */
961     offset = ((off_t)offset_high << 32) | offset_low;
962     if ((pos = lseek( fd, offset, SEEK_SET )) == -1)
963     {
964         munmap( ret, size );
965         return (LPVOID)-1;
966     }
967     read( fd, ret, size );
968     lseek( fd, pos, SEEK_SET );  /* Restore the file pointer */
969     mprotect( ret, size, prot );  /* Set the right protection */
970     return ret;
971 }
972
973
974 /***********************************************************************
975  *             VirtualAlloc   (KERNEL32.@)
976  * Reserves or commits a region of pages in virtual address space
977  *
978  * RETURNS
979  *      Base address of allocated region of pages
980  *      NULL: Failure
981  */
982 LPVOID WINAPI VirtualAlloc(
983               LPVOID addr,  /* [in] Address of region to reserve or commit */
984               DWORD size,   /* [in] Size of region */
985               DWORD type,   /* [in] Type of allocation */
986               DWORD protect)/* [in] Type of access protection */
987 {
988     FILE_VIEW *view;
989     char *ptr, *base;
990     BYTE vprot;
991
992     TRACE("%p %08lx %lx %08lx\n", addr, size, type, protect );
993
994     /* Round parameters to a page boundary */
995
996     if (size > 0x7fc00000)  /* 2Gb - 4Mb */
997     {
998         SetLastError( ERROR_OUTOFMEMORY );
999         return NULL;
1000     }
1001     if (addr)
1002     {
1003         if (type & MEM_RESERVE) /* Round down to 64k boundary */
1004             base = ROUND_ADDR( addr, granularity_mask );
1005         else
1006             base = ROUND_ADDR( addr, page_mask );
1007         size = (((UINT_PTR)addr + size + page_mask) & ~page_mask) - (UINT_PTR)base;
1008
1009         /* disallow low 64k, wrap-around and kernel space */
1010         if ((base <= (char *)granularity_mask) ||
1011             (base + size < base) ||
1012             (base + size > (char *)ADDRESS_SPACE_LIMIT))
1013         {
1014             SetLastError( ERROR_INVALID_PARAMETER );
1015             return NULL;
1016         }
1017     }
1018     else
1019     {
1020         base = 0;
1021         size = (size + page_mask) & ~page_mask;
1022     }
1023
1024     if (type & MEM_TOP_DOWN) {
1025         /* FIXME: MEM_TOP_DOWN allocates the largest possible address.
1026          *        Is there _ANY_ way to do it with UNIX mmap()?
1027          */
1028         WARN("MEM_TOP_DOWN ignored\n");
1029         type &= ~MEM_TOP_DOWN;
1030     }
1031     /* Compute the alloc type flags */
1032
1033     if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_SYSTEM)) ||
1034         (type & ~(MEM_COMMIT | MEM_RESERVE | MEM_SYSTEM)))
1035     {
1036         ERR("called with wrong alloc type flags (%08lx) !\n", type);
1037         SetLastError( ERROR_INVALID_PARAMETER );
1038         return NULL;
1039     }
1040     if (type & (MEM_COMMIT | MEM_SYSTEM))
1041         vprot = VIRTUAL_GetProt( protect ) | VPROT_COMMITTED;
1042     else vprot = 0;
1043
1044     /* Reserve the memory */
1045
1046     if ((type & MEM_RESERVE) || !base)
1047     {
1048         if (type & MEM_SYSTEM)
1049         {
1050             if (!(view = VIRTUAL_CreateView( base, size, VFLAG_VALLOC | VFLAG_SYSTEM, vprot, 0 )))
1051             {
1052                 SetLastError( ERROR_OUTOFMEMORY );
1053                 return NULL;
1054             }
1055             return (LPVOID)base;
1056         }
1057         ptr = anon_mmap_aligned( base, size, VIRTUAL_GetUnixProt( vprot ), 0 );
1058         if (ptr == (void *)-1) return NULL;
1059
1060         if (!(view = VIRTUAL_CreateView( ptr, size, VFLAG_VALLOC, vprot, 0 )))
1061         {
1062             munmap( ptr, size );
1063             SetLastError( ERROR_OUTOFMEMORY );
1064             return NULL;
1065         }
1066         return ptr;
1067     }
1068
1069     /* Commit the pages */
1070
1071     if (!(view = VIRTUAL_FindView( base )) ||
1072         (base + size > (char *)view->base + view->size))
1073     {
1074         SetLastError( ERROR_INVALID_ADDRESS );
1075         return NULL;
1076     }
1077
1078     if (!VIRTUAL_SetProt( view, base, size, vprot )) return NULL;
1079     return (LPVOID)base;
1080 }
1081
1082
1083 /***********************************************************************
1084  *             VirtualAllocEx   (KERNEL32.@)
1085  *
1086  * Seems to be just as VirtualAlloc, but with process handle.
1087  */
1088 LPVOID WINAPI VirtualAllocEx(
1089               HANDLE hProcess, /* [in] Handle of process to do mem operation */
1090               LPVOID addr,  /* [in] Address of region to reserve or commit */
1091               DWORD size,   /* [in] Size of region */
1092               DWORD type,   /* [in] Type of allocation */
1093               DWORD protect /* [in] Type of access protection */
1094 ) {
1095     if (MapProcessHandle( hProcess ) == GetCurrentProcessId())
1096         return VirtualAlloc( addr, size, type, protect );
1097     ERR("Unsupported on other process\n");
1098     return NULL;
1099 }
1100
1101
1102 /***********************************************************************
1103  *             VirtualFree   (KERNEL32.@)
1104  * Release or decommits a region of pages in virtual address space.
1105  *
1106  * RETURNS
1107  *      TRUE: Success
1108  *      FALSE: Failure
1109  */
1110 BOOL WINAPI VirtualFree(
1111               LPVOID addr, /* [in] Address of region of committed pages */
1112               DWORD size,  /* [in] Size of region */
1113               DWORD type   /* [in] Type of operation */
1114 ) {
1115     FILE_VIEW *view;
1116     char *base;
1117
1118     TRACE("%p %08lx %lx\n", addr, size, type );
1119
1120     /* Fix the parameters */
1121
1122     size = ROUND_SIZE( addr, size );
1123     base = ROUND_ADDR( addr, page_mask );
1124
1125     if (!(view = VIRTUAL_FindView( base )) ||
1126         (base + size > (char *)view->base + view->size) ||
1127         !(view->flags & VFLAG_VALLOC))
1128     {
1129         SetLastError( ERROR_INVALID_PARAMETER );
1130         return FALSE;
1131     }
1132
1133     /* Check the type */
1134
1135     if (type & MEM_SYSTEM)
1136     {
1137         view->flags |= VFLAG_SYSTEM;
1138         type &= ~MEM_SYSTEM;
1139     }
1140
1141     if ((type != MEM_DECOMMIT) && (type != MEM_RELEASE))
1142     {
1143         ERR("called with wrong free type flags (%08lx) !\n", type);
1144         SetLastError( ERROR_INVALID_PARAMETER );
1145         return FALSE;
1146     }
1147
1148     /* Free the pages */
1149
1150     if (type == MEM_RELEASE)
1151     {
1152         if (size || (base != view->base))
1153         {
1154             SetLastError( ERROR_INVALID_PARAMETER );
1155             return FALSE;
1156         }
1157         VIRTUAL_DeleteView( view );
1158         return TRUE;
1159     }
1160
1161     /* Decommit the pages by remapping zero-pages instead */
1162
1163     if (wine_anon_mmap( (LPVOID)base, size, VIRTUAL_GetUnixProt(0), MAP_FIXED ) != (LPVOID)base)
1164         ERR( "Could not remap pages, expect trouble\n" );
1165     return VIRTUAL_SetProt( view, base, size, 0 );
1166 }
1167
1168
1169 /***********************************************************************
1170  *             VirtualLock   (KERNEL32.@)
1171  * Locks the specified region of virtual address space
1172  *
1173  * NOTE
1174  *      Always returns TRUE
1175  *
1176  * RETURNS
1177  *      TRUE: Success
1178  *      FALSE: Failure
1179  */
1180 BOOL WINAPI VirtualLock(
1181               LPVOID addr, /* [in] Address of first byte of range to lock */
1182               DWORD size   /* [in] Number of bytes in range to lock */
1183 ) {
1184     return TRUE;
1185 }
1186
1187
1188 /***********************************************************************
1189  *             VirtualUnlock   (KERNEL32.@)
1190  * Unlocks a range of pages in the virtual address space
1191  *
1192  * NOTE
1193  *      Always returns TRUE
1194  *
1195  * RETURNS
1196  *      TRUE: Success
1197  *      FALSE: Failure
1198  */
1199 BOOL WINAPI VirtualUnlock(
1200               LPVOID addr, /* [in] Address of first byte of range */
1201               DWORD size   /* [in] Number of bytes in range */
1202 ) {
1203     return TRUE;
1204 }
1205
1206
1207 /***********************************************************************
1208  *             VirtualProtect   (KERNEL32.@)
1209  * Changes the access protection on a region of committed pages
1210  *
1211  * RETURNS
1212  *      TRUE: Success
1213  *      FALSE: Failure
1214  */
1215 BOOL WINAPI VirtualProtect(
1216               LPVOID addr,     /* [in] Address of region of committed pages */
1217               DWORD size,      /* [in] Size of region */
1218               DWORD new_prot,  /* [in] Desired access protection */
1219               LPDWORD old_prot /* [out] Address of variable to get old protection */
1220 ) {
1221     FILE_VIEW *view;
1222     char *base;
1223     UINT i;
1224     BYTE vprot, *p;
1225     DWORD prot;
1226
1227     TRACE("%p %08lx %08lx\n", addr, size, new_prot );
1228
1229     /* Fix the parameters */
1230
1231     size = ROUND_SIZE( addr, size );
1232     base = ROUND_ADDR( addr, page_mask );
1233
1234     if (!(view = VIRTUAL_FindView( base )) ||
1235         (base + size > (char *)view->base + view->size))
1236     {
1237         SetLastError( ERROR_INVALID_PARAMETER );
1238         return FALSE;
1239     }
1240
1241     /* Make sure all the pages are committed */
1242
1243     p = view->prot + ((base - (char *)view->base) >> page_shift);
1244     VIRTUAL_GetWin32Prot( *p, &prot, NULL );
1245     for (i = size >> page_shift; i; i--, p++)
1246     {
1247         if (!(*p & VPROT_COMMITTED))
1248         {
1249             SetLastError( ERROR_INVALID_PARAMETER );
1250             return FALSE;
1251         }
1252     }
1253
1254     if (old_prot) *old_prot = prot;
1255     vprot = VIRTUAL_GetProt( new_prot ) | VPROT_COMMITTED;
1256     return VIRTUAL_SetProt( view, base, size, vprot );
1257 }
1258
1259
1260 /***********************************************************************
1261  *             VirtualProtectEx   (KERNEL32.@)
1262  * Changes the access protection on a region of committed pages in the
1263  * virtual address space of a specified process
1264  *
1265  * RETURNS
1266  *      TRUE: Success
1267  *      FALSE: Failure
1268  */
1269 BOOL WINAPI VirtualProtectEx(
1270               HANDLE handle, /* [in]  Handle of process */
1271               LPVOID addr,     /* [in]  Address of region of committed pages */
1272               DWORD size,      /* [in]  Size of region */
1273               DWORD new_prot,  /* [in]  Desired access protection */
1274               LPDWORD old_prot /* [out] Address of variable to get old protection */ )
1275 {
1276     if (MapProcessHandle( handle ) == GetCurrentProcessId())
1277         return VirtualProtect( addr, size, new_prot, old_prot );
1278     ERR("Unsupported on other process\n");
1279     return FALSE;
1280 }
1281
1282
1283 /***********************************************************************
1284  *             VirtualQuery   (KERNEL32.@)
1285  * Provides info about a range of pages in virtual address space
1286  *
1287  * RETURNS
1288  *      Number of bytes returned in information buffer
1289  *      or 0 if addr is >= 0xc0000000 (kernel space).
1290  */
1291 DWORD WINAPI VirtualQuery(
1292              LPCVOID addr,                    /* [in]  Address of region */
1293              LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
1294              DWORD len                        /* [in]  Size of buffer */
1295 ) {
1296     FILE_VIEW *view;
1297     char *base, *alloc_base = 0;
1298     UINT size = 0;
1299
1300     if (addr >= ADDRESS_SPACE_LIMIT) return 0;
1301
1302     base = ROUND_ADDR( addr, page_mask );
1303
1304     /* Find the view containing the address */
1305
1306     EnterCriticalSection(&csVirtual);
1307     view = VIRTUAL_FirstView;
1308     for (;;)
1309     {
1310         if (!view)
1311         {
1312             size = (char *)ADDRESS_SPACE_LIMIT - alloc_base;
1313             break;
1314         }
1315         if ((char *)view->base > base)
1316         {
1317             size = (char *)view->base - alloc_base;
1318             view = NULL;
1319             break;
1320         }
1321         if ((char *)view->base + view->size > base)
1322         {
1323             alloc_base = view->base;
1324             size = view->size;
1325             break;
1326         }
1327         alloc_base = (char *)view->base + view->size;
1328         view = view->next;
1329     }
1330     LeaveCriticalSection(&csVirtual);
1331
1332     /* Fill the info structure */
1333
1334     if (!view)
1335     {
1336         info->State             = MEM_FREE;
1337         info->Protect           = 0;
1338         info->AllocationProtect = 0;
1339         info->Type              = 0;
1340     }
1341     else
1342     {
1343         BYTE vprot = view->prot[(base - alloc_base) >> page_shift];
1344         VIRTUAL_GetWin32Prot( vprot, &info->Protect, &info->State );
1345         for (size = base - alloc_base; size < view->size; size += page_mask+1)
1346             if (view->prot[size >> page_shift] != vprot) break;
1347         info->AllocationProtect = view->protect;
1348         info->Type              = MEM_PRIVATE;  /* FIXME */
1349     }
1350
1351     info->BaseAddress    = (LPVOID)base;
1352     info->AllocationBase = (LPVOID)alloc_base;
1353     info->RegionSize     = size - (base - alloc_base);
1354     return sizeof(*info);
1355 }
1356
1357
1358 /***********************************************************************
1359  *             VirtualQueryEx   (KERNEL32.@)
1360  * Provides info about a range of pages in virtual address space of a
1361  * specified process
1362  *
1363  * RETURNS
1364  *      Number of bytes returned in information buffer
1365  */
1366 DWORD WINAPI VirtualQueryEx(
1367              HANDLE handle,                 /* [in] Handle of process */
1368              LPCVOID addr,                    /* [in] Address of region */
1369              LPMEMORY_BASIC_INFORMATION info, /* [out] Address of info buffer */
1370              DWORD len                        /* [in] Size of buffer */ )
1371 {
1372     if (MapProcessHandle( handle ) == GetCurrentProcessId())
1373         return VirtualQuery( addr, info, len );
1374     ERR("Unsupported on other process\n");
1375     return 0;
1376 }
1377
1378
1379 /***********************************************************************
1380  *             IsBadReadPtr   (KERNEL32.@)
1381  *
1382  * RETURNS
1383  *      FALSE: Process has read access to entire block
1384  *      TRUE: Otherwise
1385  */
1386 BOOL WINAPI IsBadReadPtr(
1387               LPCVOID ptr, /* [in] Address of memory block */
1388               UINT size )  /* [in] Size of block */
1389 {
1390     if (!size) return FALSE;  /* handle 0 size case w/o reference */
1391     __TRY
1392     {
1393         volatile const char *p = ptr;
1394         char dummy;
1395         UINT count = size;
1396
1397         while (count > page_size)
1398         {
1399             dummy = *p;
1400             p += page_size;
1401             count -= page_size;
1402         }
1403         dummy = p[0];
1404         dummy = p[count - 1];
1405     }
1406     __EXCEPT(page_fault) { return TRUE; }
1407     __ENDTRY
1408     return FALSE;
1409 }
1410
1411
1412 /***********************************************************************
1413  *             IsBadWritePtr   (KERNEL32.@)
1414  *
1415  * RETURNS
1416  *      FALSE: Process has write access to entire block
1417  *      TRUE: Otherwise
1418  */
1419 BOOL WINAPI IsBadWritePtr(
1420               LPVOID ptr, /* [in] Address of memory block */
1421               UINT size ) /* [in] Size of block in bytes */
1422 {
1423     if (!size) return FALSE;  /* handle 0 size case w/o reference */
1424     __TRY
1425     {
1426         volatile char *p = ptr;
1427         UINT count = size;
1428
1429         while (count > page_size)
1430         {
1431             *p |= 0;
1432             p += page_size;
1433             count -= page_size;
1434         }
1435         p[0] |= 0;
1436         p[count - 1] |= 0;
1437     }
1438     __EXCEPT(page_fault) { return TRUE; }
1439     __ENDTRY
1440     return FALSE;
1441 }
1442
1443
1444 /***********************************************************************
1445  *             IsBadHugeReadPtr   (KERNEL32.@)
1446  * RETURNS
1447  *      FALSE: Process has read access to entire block
1448  *      TRUE: Otherwise
1449  */
1450 BOOL WINAPI IsBadHugeReadPtr(
1451               LPCVOID ptr, /* [in] Address of memory block */
1452               UINT size  /* [in] Size of block */
1453 ) {
1454     return IsBadReadPtr( ptr, size );
1455 }
1456
1457
1458 /***********************************************************************
1459  *             IsBadHugeWritePtr   (KERNEL32.@)
1460  * RETURNS
1461  *      FALSE: Process has write access to entire block
1462  *      TRUE: Otherwise
1463  */
1464 BOOL WINAPI IsBadHugeWritePtr(
1465               LPVOID ptr, /* [in] Address of memory block */
1466               UINT size /* [in] Size of block */
1467 ) {
1468     return IsBadWritePtr( ptr, size );
1469 }
1470
1471
1472 /***********************************************************************
1473  *             IsBadCodePtr   (KERNEL32.@)
1474  *
1475  * RETURNS
1476  *      FALSE: Process has read access to specified memory
1477  *      TRUE: Otherwise
1478  */
1479 BOOL WINAPI IsBadCodePtr( FARPROC ptr ) /* [in] Address of function */
1480 {
1481     return IsBadReadPtr( ptr, 1 );
1482 }
1483
1484
1485 /***********************************************************************
1486  *             IsBadStringPtrA   (KERNEL32.@)
1487  *
1488  * RETURNS
1489  *      FALSE: Read access to all bytes in string
1490  *      TRUE: Else
1491  */
1492 BOOL WINAPI IsBadStringPtrA(
1493               LPCSTR str, /* [in] Address of string */
1494               UINT max )  /* [in] Maximum size of string */
1495 {
1496     __TRY
1497     {
1498         volatile const char *p = str;
1499         while (p != str + max) if (!*p++) break;
1500     }
1501     __EXCEPT(page_fault) { return TRUE; }
1502     __ENDTRY
1503     return FALSE;
1504 }
1505
1506
1507 /***********************************************************************
1508  *             IsBadStringPtrW   (KERNEL32.@)
1509  * See IsBadStringPtrA
1510  */
1511 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT max )
1512 {
1513     __TRY
1514     {
1515         volatile const WCHAR *p = str;
1516         while (p != str + max) if (!*p++) break;
1517     }
1518     __EXCEPT(page_fault) { return TRUE; }
1519     __ENDTRY
1520     return FALSE;
1521 }
1522
1523
1524 /***********************************************************************
1525  *             CreateFileMappingA   (KERNEL32.@)
1526  * Creates a named or unnamed file-mapping object for the specified file
1527  *
1528  * RETURNS
1529  *      Handle: Success
1530  *      0: Mapping object does not exist
1531  *      NULL: Failure
1532  */
1533 HANDLE WINAPI CreateFileMappingA(
1534                 HANDLE hFile,   /* [in] Handle of file to map */
1535                 SECURITY_ATTRIBUTES *sa, /* [in] Optional security attributes*/
1536                 DWORD protect,   /* [in] Protection for mapping object */
1537                 DWORD size_high, /* [in] High-order 32 bits of object size */
1538                 DWORD size_low,  /* [in] Low-order 32 bits of object size */
1539                 LPCSTR name      /* [in] Name of file-mapping object */ )
1540 {
1541     WCHAR buffer[MAX_PATH];
1542
1543     if (!name) return CreateFileMappingW( hFile, sa, protect, size_high, size_low, NULL );
1544
1545     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
1546     {
1547         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1548         return 0;
1549     }
1550     return CreateFileMappingW( hFile, sa, protect, size_high, size_low, buffer );
1551 }
1552
1553
1554 /***********************************************************************
1555  *             CreateFileMappingW   (KERNEL32.@)
1556  * See CreateFileMappingA
1557  */
1558 HANDLE WINAPI CreateFileMappingW( HANDLE hFile, LPSECURITY_ATTRIBUTES sa,
1559                                   DWORD protect, DWORD size_high,
1560                                   DWORD size_low, LPCWSTR name )
1561 {
1562     HANDLE ret;
1563     BYTE vprot;
1564     DWORD len = name ? strlenW(name) : 0;
1565
1566     /* Check parameters */
1567
1568     TRACE("(%x,%p,%08lx,%08lx%08lx,%s)\n",
1569           hFile, sa, protect, size_high, size_low, debugstr_w(name) );
1570
1571     if (len > MAX_PATH)
1572     {
1573         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1574         return 0;
1575     }
1576
1577     vprot = VIRTUAL_GetProt( protect );
1578     if (protect & SEC_RESERVE)
1579     {
1580         if (hFile != INVALID_HANDLE_VALUE)
1581         {
1582             SetLastError( ERROR_INVALID_PARAMETER );
1583             return 0;
1584         }
1585     }
1586     else vprot |= VPROT_COMMITTED;
1587     if (protect & SEC_NOCACHE) vprot |= VPROT_NOCACHE;
1588     if (protect & SEC_IMAGE) vprot |= VPROT_IMAGE;
1589
1590     /* Create the server object */
1591
1592     if (hFile == INVALID_HANDLE_VALUE) hFile = 0;
1593     SERVER_START_REQ( create_mapping )
1594     {
1595         req->file_handle = hFile;
1596         req->size_high   = size_high;
1597         req->size_low    = size_low;
1598         req->protect     = vprot;
1599         req->inherit     = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
1600         wine_server_add_data( req, name, len * sizeof(WCHAR) );
1601         SetLastError(0);
1602         wine_server_call_err( req );
1603         ret = reply->handle;
1604     }
1605     SERVER_END_REQ;
1606     return ret;
1607 }
1608
1609
1610 /***********************************************************************
1611  *             OpenFileMappingA   (KERNEL32.@)
1612  * Opens a named file-mapping object.
1613  *
1614  * RETURNS
1615  *      Handle: Success
1616  *      NULL: Failure
1617  */
1618 HANDLE WINAPI OpenFileMappingA(
1619                 DWORD access,   /* [in] Access mode */
1620                 BOOL inherit, /* [in] Inherit flag */
1621                 LPCSTR name )   /* [in] Name of file-mapping object */
1622 {
1623     WCHAR buffer[MAX_PATH];
1624
1625     if (!name) return OpenFileMappingW( access, inherit, NULL );
1626
1627     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
1628     {
1629         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1630         return 0;
1631     }
1632     return OpenFileMappingW( access, inherit, buffer );
1633 }
1634
1635
1636 /***********************************************************************
1637  *             OpenFileMappingW   (KERNEL32.@)
1638  * See OpenFileMappingA
1639  */
1640 HANDLE WINAPI OpenFileMappingW( DWORD access, BOOL inherit, LPCWSTR name)
1641 {
1642     HANDLE ret;
1643     DWORD len = name ? strlenW(name) : 0;
1644     if (len >= MAX_PATH)
1645     {
1646         SetLastError( ERROR_FILENAME_EXCED_RANGE );
1647         return 0;
1648     }
1649     SERVER_START_REQ( open_mapping )
1650     {
1651         req->access  = access;
1652         req->inherit = inherit;
1653         wine_server_add_data( req, name, len * sizeof(WCHAR) );
1654         wine_server_call_err( req );
1655         ret = reply->handle;
1656     }
1657     SERVER_END_REQ;
1658     return ret;
1659 }
1660
1661
1662 /***********************************************************************
1663  *             MapViewOfFile   (KERNEL32.@)
1664  * Maps a view of a file into the address space
1665  *
1666  * RETURNS
1667  *      Starting address of mapped view
1668  *      NULL: Failure
1669  */
1670 LPVOID WINAPI MapViewOfFile(
1671               HANDLE mapping,  /* [in] File-mapping object to map */
1672               DWORD access,      /* [in] Access mode */
1673               DWORD offset_high, /* [in] High-order 32 bits of file offset */
1674               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
1675               DWORD count        /* [in] Number of bytes to map */
1676 ) {
1677     return MapViewOfFileEx( mapping, access, offset_high,
1678                             offset_low, count, NULL );
1679 }
1680
1681
1682 /***********************************************************************
1683  *             MapViewOfFileEx   (KERNEL32.@)
1684  * Maps a view of a file into the address space
1685  *
1686  * RETURNS
1687  *      Starting address of mapped view
1688  *      NULL: Failure
1689  */
1690 LPVOID WINAPI MapViewOfFileEx(
1691               HANDLE handle,   /* [in] File-mapping object to map */
1692               DWORD access,      /* [in] Access mode */
1693               DWORD offset_high, /* [in] High-order 32 bits of file offset */
1694               DWORD offset_low,  /* [in] Low-order 32 bits of file offset */
1695               DWORD count,       /* [in] Number of bytes to map */
1696               LPVOID addr        /* [in] Suggested starting address for mapped view */
1697 ) {
1698     FILE_VIEW *view;
1699     UINT size = 0;
1700     int flags = MAP_PRIVATE;
1701     int unix_handle = -1;
1702     int prot, res;
1703     void *base, *ptr = (void *)-1, *ret;
1704     DWORD size_low, size_high, header_size, shared_size;
1705     HANDLE shared_file;
1706     BOOL removable;
1707
1708     /* Check parameters */
1709
1710     if ((offset_low & granularity_mask) ||
1711         (addr && ((UINT_PTR)addr & granularity_mask)))
1712     {
1713         SetLastError( ERROR_INVALID_PARAMETER );
1714         return NULL;
1715     }
1716
1717     SERVER_START_REQ( get_mapping_info )
1718     {
1719         req->handle = handle;
1720         res = wine_server_call_err( req );
1721         prot        = reply->protect;
1722         base        = reply->base;
1723         size_low    = reply->size_low;
1724         size_high   = reply->size_high;
1725         header_size = reply->header_size;
1726         shared_file = reply->shared_file;
1727         shared_size = reply->shared_size;
1728         removable   = (reply->drive_type == DRIVE_REMOVABLE ||
1729                        reply->drive_type == DRIVE_CDROM);
1730     }
1731     SERVER_END_REQ;
1732     if (res) goto error;
1733
1734     if ((unix_handle = FILE_GetUnixHandle( handle, 0 )) == -1) goto error;
1735
1736     if (prot & VPROT_IMAGE)
1737         return map_image( handle, unix_handle, base, size_low, header_size,
1738                           shared_file, shared_size, removable );
1739
1740
1741     if (size_high)
1742         ERR("Sizes larger than 4Gb not supported\n");
1743
1744     if ((offset_low >= size_low) ||
1745         (count > size_low - offset_low))
1746     {
1747         SetLastError( ERROR_INVALID_PARAMETER );
1748         goto error;
1749     }
1750     if (count) size = ROUND_SIZE( offset_low, count );
1751     else size = size_low - offset_low;
1752
1753     switch(access)
1754     {
1755     case FILE_MAP_ALL_ACCESS:
1756     case FILE_MAP_WRITE:
1757     case FILE_MAP_WRITE | FILE_MAP_READ:
1758         if (!(prot & VPROT_WRITE))
1759         {
1760             SetLastError( ERROR_INVALID_PARAMETER );
1761             goto error;
1762         }
1763         flags = MAP_SHARED;
1764         /* fall through */
1765     case FILE_MAP_READ:
1766     case FILE_MAP_COPY:
1767     case FILE_MAP_COPY | FILE_MAP_READ:
1768         if (prot & VPROT_READ) break;
1769         /* fall through */
1770     default:
1771         SetLastError( ERROR_INVALID_PARAMETER );
1772         goto error;
1773     }
1774
1775     /* FIXME: If a mapping is created with SEC_RESERVE and a process,
1776      * which has a view of this mapping commits some pages, they will
1777      * appear commited in all other processes, which have the same
1778      * view created. Since we don`t support this yet, we create the
1779      * whole mapping commited.
1780      */
1781     prot |= VPROT_COMMITTED;
1782
1783     /* Reserve a properly aligned area */
1784
1785     if ((ptr = anon_mmap_aligned( addr, size, PROT_NONE, 0 )) == (void *)-1) goto error;
1786
1787     /* Map the file */
1788
1789     TRACE("handle=%x size=%x offset=%lx\n", handle, size, offset_low );
1790
1791     ret = VIRTUAL_mmap( unix_handle, ptr, size, offset_low, offset_high,
1792                         VIRTUAL_GetUnixProt( prot ), flags | MAP_FIXED, &removable );
1793     if (ret != ptr)
1794     {
1795         ERR( "VIRTUAL_mmap %p %x %lx%08lx failed\n", ptr, size, offset_high, offset_low );
1796         goto error;
1797     }
1798     if (removable) handle = 0;  /* don't keep handle open on removable media */
1799
1800     if (!(view = VIRTUAL_CreateView( ptr, size, 0, prot, handle )))
1801     {
1802         SetLastError( ERROR_OUTOFMEMORY );
1803         goto error;
1804     }
1805     if (unix_handle != -1) close( unix_handle );
1806     return ptr;
1807
1808 error:
1809     if (unix_handle != -1) close( unix_handle );
1810     if (ptr != (void *)-1) munmap( ptr, size );
1811     return NULL;
1812 }
1813
1814
1815 /***********************************************************************
1816  *             FlushViewOfFile   (KERNEL32.@)
1817  * Writes to the disk a byte range within a mapped view of a file
1818  *
1819  * RETURNS
1820  *      TRUE: Success
1821  *      FALSE: Failure
1822  */
1823 BOOL WINAPI FlushViewOfFile(
1824               LPCVOID base, /* [in] Start address of byte range to flush */
1825               DWORD cbFlush /* [in] Number of bytes in range */
1826 ) {
1827     FILE_VIEW *view;
1828     void *addr = ROUND_ADDR( base, page_mask );
1829
1830     TRACE("FlushViewOfFile at %p for %ld bytes\n",
1831                      base, cbFlush );
1832
1833     if (!(view = VIRTUAL_FindView( addr )))
1834     {
1835         SetLastError( ERROR_INVALID_PARAMETER );
1836         return FALSE;
1837     }
1838     if (!cbFlush) cbFlush = view->size;
1839     if (!msync( addr, cbFlush, MS_SYNC )) return TRUE;
1840     SetLastError( ERROR_INVALID_PARAMETER );
1841     return FALSE;
1842 }
1843
1844
1845 /***********************************************************************
1846  *             UnmapViewOfFile   (KERNEL32.@)
1847  * Unmaps a mapped view of a file.
1848  *
1849  * NOTES
1850  *      Should addr be an LPCVOID?
1851  *
1852  * RETURNS
1853  *      TRUE: Success
1854  *      FALSE: Failure
1855  */
1856 BOOL WINAPI UnmapViewOfFile(
1857               LPVOID addr /* [in] Address where mapped view begins */
1858 ) {
1859     FILE_VIEW *view;
1860     void *base = ROUND_ADDR( addr, page_mask );
1861     if (!(view = VIRTUAL_FindView( base )) || (base != view->base))
1862     {
1863         SetLastError( ERROR_INVALID_PARAMETER );
1864         return FALSE;
1865     }
1866     VIRTUAL_DeleteView( view );
1867     return TRUE;
1868 }