virtual.c 97.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Win32 virtual memory functions
 *
 * Copyright 1997, 2002 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23 24 25 26 27 28 29 30 31 32
 */

#include "config.h"
#include "wine/port.h"

#include <assert.h>
#include <errno.h>
#ifdef HAVE_SYS_ERRNO_H
#include <sys/errno.h>
#endif
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
33
#include <stdarg.h>
34 35 36 37
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
38 39 40
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
41
#ifdef HAVE_SYS_MMAN_H
42
# include <sys/mman.h>
43
#endif
44 45
#ifdef HAVE_VALGRIND_VALGRIND_H
# include <valgrind/valgrind.h>
46
#endif
47

48 49
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
50
#include "ntstatus.h"
51
#define WIN32_NO_STATUS
52
#include "windef.h"
53 54 55
#include "winternl.h"
#include "wine/library.h"
#include "wine/server.h"
56
#include "wine/exception.h"
57
#include "wine/list.h"
58
#include "wine/debug.h"
59
#include "ntdll_misc.h"
60 61 62 63 64 65 66 67

WINE_DEFAULT_DEBUG_CHANNEL(virtual);
WINE_DECLARE_DEBUG_CHANNEL(module);

#ifndef MS_SYNC
#define MS_SYNC 0
#endif

68 69 70 71
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif

72
/* File view */
73
struct file_view
74
{
75
    struct list   entry;       /* Entry in global view list */
76
    void         *base;        /* Base address */
77
    size_t        size;        /* Size in bytes */
78
    HANDLE        mapping;     /* Handle to the file mapping */
79
    unsigned int  protect;     /* Protection for all pages at allocation time */
80
    BYTE          prot[1];     /* Protection byte for each page */
81
};
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104


/* Conversion from VPROT_* to Win32 flags */
static const BYTE VIRTUAL_Win32Flags[16] =
{
    PAGE_NOACCESS,              /* 0 */
    PAGE_READONLY,              /* READ */
    PAGE_READWRITE,             /* WRITE */
    PAGE_READWRITE,             /* READ | WRITE */
    PAGE_EXECUTE,               /* EXEC */
    PAGE_EXECUTE_READ,          /* READ | EXEC */
    PAGE_EXECUTE_READWRITE,     /* WRITE | EXEC */
    PAGE_EXECUTE_READWRITE,     /* READ | WRITE | EXEC */
    PAGE_WRITECOPY,             /* WRITECOPY */
    PAGE_WRITECOPY,             /* READ | WRITECOPY */
    PAGE_WRITECOPY,             /* WRITE | WRITECOPY */
    PAGE_WRITECOPY,             /* READ | WRITE | WRITECOPY */
    PAGE_EXECUTE_WRITECOPY,     /* EXEC | WRITECOPY */
    PAGE_EXECUTE_WRITECOPY,     /* READ | EXEC | WRITECOPY */
    PAGE_EXECUTE_WRITECOPY,     /* WRITE | EXEC | WRITECOPY */
    PAGE_EXECUTE_WRITECOPY      /* READ | WRITE | EXEC | WRITECOPY */
};

105
static struct list views_list = LIST_INIT(views_list);
106

107 108
static RTL_CRITICAL_SECTION csVirtual;
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
109 110 111
{
    0, 0, &csVirtual,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
112
      0, 0, { (DWORD_PTR)(__FILE__ ": csVirtual") }
113
};
114
static RTL_CRITICAL_SECTION csVirtual = { &critsect_debug, -1, 0, 0, 0, 0 };
115 116 117 118 119 120

#ifdef __i386__
/* These are always the same on an i386, and it will be faster this way */
# define page_mask  0xfff
# define page_shift 12
# define page_size  0x1000
121
/* Note: these are Windows limits, you cannot change them. */
122 123
static void *address_space_limit = (void *)0xc0000000;  /* top of the total available address space */
static void *user_space_limit    = (void *)0x7fff0000;  /* top of the user address space */
124
static void *working_set_limit   = (void *)0x7fff0000;  /* top of the current working set */
125
static void *address_space_start = (void *)0x110000;    /* keep DOS area clear */
126 127 128 129 130 131 132
#elif defined(__x86_64__)
# define page_mask  0xfff
# define page_shift 12
# define page_size  0x1000
static void *address_space_limit = (void *)0x7fffffff0000;
static void *user_space_limit    = (void *)0x7fffffff0000;
static void *working_set_limit   = (void *)0x7fffffff0000;
133
static void *address_space_start = (void *)0x10000;
134 135
#else
static UINT page_shift;
136
static UINT_PTR page_size;
137
static UINT_PTR page_mask;
138 139 140
static void *address_space_limit;
static void *user_space_limit;
static void *working_set_limit;
141
static void *address_space_start = (void *)0x10000;
142
#endif  /* __i386__ */
143
static const int is_win64 = (sizeof(void *) > sizeof(int));
144 145

#define ROUND_ADDR(addr,mask) \
146
   ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask)))
147 148

#define ROUND_SIZE(addr,size) \
149
   (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
150 151

#define VIRTUAL_DEBUG_DUMP_VIEW(view) \
152
    do { if (TRACE_ON(virtual)) VIRTUAL_DumpView(view); } while (0)
153

154 155 156
#define VIRTUAL_HEAP_SIZE (4*1024*1024)

static HANDLE virtual_heap;
157 158
static void *preload_reserve_start;
static void *preload_reserve_end;
159
static int use_locks;
160
static int force_exec_prot;  /* whether to force PROT_EXEC on all PROT_READ mmaps */
161

162 163 164 165 166 167 168 169

/***********************************************************************
 *           VIRTUAL_GetProtStr
 */
static const char *VIRTUAL_GetProtStr( BYTE prot )
{
    static char buffer[6];
    buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-';
170
    buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-');
171
    buffer[2] = (prot & VPROT_READ) ? 'r' : '-';
172
    buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-');
173 174 175 176 177 178
    buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-';
    buffer[5] = 0;
    return buffer;
}


179 180 181 182 183 184 185 186 187 188 189 190 191 192
/***********************************************************************
 *           VIRTUAL_GetUnixProt
 *
 * Convert page protections to protection for mmap/mprotect.
 */
static int VIRTUAL_GetUnixProt( BYTE vprot )
{
    int prot = 0;
    if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD))
    {
        if (vprot & VPROT_READ) prot |= PROT_READ;
        if (vprot & VPROT_WRITE) prot |= PROT_WRITE;
        if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE;
        if (vprot & VPROT_EXEC) prot |= PROT_EXEC;
193
        if (vprot & VPROT_WRITEWATCH) prot &= ~PROT_WRITE;
194 195 196 197 198 199
    }
    if (!prot) prot = PROT_NONE;
    return prot;
}


200 201 202
/***********************************************************************
 *           VIRTUAL_DumpView
 */
203
static void VIRTUAL_DumpView( struct file_view *view )
204 205 206 207 208
{
    UINT i, count;
    char *addr = view->base;
    BYTE prot = view->prot[0];

209
    TRACE( "View: %p - %p", addr, addr + view->size - 1 );
210
    if (view->protect & VPROT_SYSTEM)
211
        TRACE( " (system)\n" );
212
    else if (view->protect & VPROT_VALLOC)
213
        TRACE( " (valloc)\n" );
214
    else if (view->mapping)
215
        TRACE( " %p\n", view->mapping );
216
    else
217
        TRACE( " (anonymous)\n");
218 219 220 221

    for (count = i = 1; i < view->size >> page_shift; i++, count++)
    {
        if (view->prot[i] == prot) continue;
222
        TRACE( "      %p - %p %s\n",
223 224 225 226 227 228
                 addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
        addr += (count << page_shift);
        prot = view->prot[i];
        count = 0;
    }
    if (count)
229
        TRACE( "      %p - %p %s\n",
230 231 232 233 234 235 236
                 addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
}


/***********************************************************************
 *           VIRTUAL_Dump
 */
237
#ifdef WINE_VM_DEBUG
238
static void VIRTUAL_Dump(void)
239
{
240
    sigset_t sigset;
241
    struct file_view *view;
242

243
    TRACE( "Dump of all virtual memory views:\n" );
244
    server_enter_uninterrupted_section( &csVirtual, &sigset );
245
    LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry )
246
    {
247
        VIRTUAL_DumpView( view );
248
    }
249
    server_leave_uninterrupted_section( &csVirtual, &sigset );
250
}
251
#endif
252 253 254 255 256


/***********************************************************************
 *           VIRTUAL_FindView
 *
257
 * Find the view containing a given address. The csVirtual section must be held by caller.
258
 *
259 260 261
 * PARAMS
 *      addr  [I] Address
 *
262 263 264 265
 * RETURNS
 *	View: Success
 *	NULL: Failure
 */
266
static struct file_view *VIRTUAL_FindView( const void *addr, size_t size )
267
{
268
    struct file_view *view;
269

270
    LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry )
271
    {
272 273 274 275 276
        if (view->base > addr) break;  /* no matching view */
        if ((const char *)view->base + view->size <= (const char *)addr) continue;
        if ((const char *)view->base + view->size < (const char *)addr + size) break;  /* size too large */
        if ((const char *)addr + size < (const char *)addr) break; /* overflow */
        return view;
277
    }
278
    return NULL;
279 280 281
}


282 283 284 285 286 287 288 289 290 291 292
/***********************************************************************
 *           get_mask
 */
static inline UINT_PTR get_mask( ULONG zero_bits )
{
    if (!zero_bits) return 0xffff;  /* allocations are aligned to 64K by default */
    if (zero_bits < page_shift) zero_bits = page_shift;
    return (1 << zero_bits) - 1;
}


293
/***********************************************************************
294
 *           find_view_range
295
 *
296 297 298 299 300
 * Find the first view overlapping at least part of the specified range.
 * The csVirtual section must be held by caller.
 */
static struct file_view *find_view_range( const void *addr, size_t size )
{
301
    struct file_view *view;
302

303
    LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry )
304
    {
Eric Pouech's avatar
Eric Pouech committed
305 306
        if ((const char *)view->base >= (const char *)addr + size) break;
        if ((const char *)view->base + view->size > (const char *)addr) return view;
307 308 309 310 311
    }
    return NULL;
}


312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
/***********************************************************************
 *           find_free_area
 *
 * Find a free area between views inside the specified range.
 * The csVirtual section must be held by caller.
 */
static void *find_free_area( void *base, void *end, size_t size, size_t mask, int top_down )
{
    struct list *ptr;
    void *start;

    if (top_down)
    {
        start = ROUND_ADDR( (char *)end - size, mask );
        if (start >= end || start < base) return NULL;

        for (ptr = views_list.prev; ptr != &views_list; ptr = ptr->prev)
        {
            struct file_view *view = LIST_ENTRY( ptr, struct file_view, entry );

            if ((char *)view->base + view->size <= (char *)start) break;
            if ((char *)view->base >= (char *)start + size) continue;
            start = ROUND_ADDR( (char *)view->base - size, mask );
            /* stop if remaining space is not large enough */
            if (!start || start >= end || start < base) return NULL;
        }
    }
    else
    {
        start = ROUND_ADDR( (char *)base + mask, mask );
        if (start >= end || (char *)end - (char *)start < size) return NULL;

        for (ptr = views_list.next; ptr != &views_list; ptr = ptr->next)
        {
            struct file_view *view = LIST_ENTRY( ptr, struct file_view, entry );

            if ((char *)view->base >= (char *)start + size) break;
            if ((char *)view->base + view->size <= (char *)start) continue;
            start = ROUND_ADDR( (char *)view->base + view->size + mask, mask );
            /* stop if remaining space is not large enough */
            if (!start || start >= end || (char *)end - (char *)start < size) return NULL;
        }
    }
    return start;
}


359 360
/***********************************************************************
 *           add_reserved_area
361
 *
362 363 364 365 366 367 368
 * Add a reserved area to the list maintained by libwine.
 * The csVirtual section must be held by caller.
 */
static void add_reserved_area( void *addr, size_t size )
{
    TRACE( "adding %p-%p\n", addr, (char *)addr + size );

369
    if (addr < user_space_limit)
370 371
    {
        /* unmap the part of the area that is below the limit */
372 373 374 375
        assert( (char *)addr + size > (char *)user_space_limit );
        munmap( addr, (char *)user_space_limit - (char *)addr );
        size -= (char *)user_space_limit - (char *)addr;
        addr = user_space_limit;
376
    }
377 378
    /* blow away existing mappings */
    wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
379 380 381 382
    wine_mmap_add_reserved_area( addr, size );
}


383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
/***********************************************************************
 *           remove_reserved_area
 *
 * Remove a reserved area from the list maintained by libwine.
 * The csVirtual section must be held by caller.
 */
static void remove_reserved_area( void *addr, size_t size )
{
    struct file_view *view;

    TRACE( "removing %p-%p\n", addr, (char *)addr + size );
    wine_mmap_remove_reserved_area( addr, size, 0 );

    /* unmap areas not covered by an existing view */
    LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry )
    {
        if ((char *)view->base >= (char *)addr + size)
        {
            munmap( addr, size );
            break;
        }
        if ((char *)view->base + view->size <= (char *)addr) continue;
        if (view->base > addr) munmap( addr, (char *)view->base - (char *)addr );
        if ((char *)view->base + view->size > (char *)addr + size) break;
        size = (char *)addr + size - ((char *)view->base + view->size);
        addr = (char *)view->base + view->size;
    }
}


413 414 415 416 417
/***********************************************************************
 *           is_beyond_limit
 *
 * Check if an address range goes beyond a given limit.
 */
418
static inline int is_beyond_limit( const void *addr, size_t size, const void *limit )
419
{
420
    return (addr >= limit || (const char *)addr + size > (const char *)limit);
421 422 423 424 425 426 427 428 429 430 431 432 433
}


/***********************************************************************
 *           unmap_area
 *
 * Unmap an area, or simply replace it by an empty mapping if it is
 * in a reserved area. The csVirtual section must be held by caller.
 */
static inline void unmap_area( void *addr, size_t size )
{
    if (wine_mmap_is_in_reserved_area( addr, size ))
        wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
434 435
    else if (is_beyond_limit( addr, size, user_space_limit ))
        add_reserved_area( addr, size );
436 437 438 439 440 441 442 443 444
    else
        munmap( addr, size );
}


/***********************************************************************
 *           delete_view
 *
 * Deletes a view. The csVirtual section must be held by caller.
445
 */
446
static void delete_view( struct file_view *view ) /* [in] View */
447
{
448
    if (!(view->protect & VPROT_SYSTEM)) unmap_area( view->base, view->size );
449
    list_remove( &view->entry );
450
    if (view->mapping) close_handle( view->mapping );
451
    RtlFreeHeap( virtual_heap, 0, view );
452 453 454
}


455
/***********************************************************************
456
 *           create_view
457
 *
458
 * Create a view. The csVirtual section must be held by caller.
459
 */
460
static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t size, unsigned int vprot )
461
{
462 463
    struct file_view *view;
    struct list *ptr;
464
    int unix_prot = VIRTUAL_GetUnixProt( vprot );
465

466
    assert( !((UINT_PTR)base & page_mask) );
467
    assert( !(size & page_mask) );
468 469 470

    /* Create the view structure */

471 472 473 474 475
    if (!(view = RtlAllocateHeap( virtual_heap, 0, sizeof(*view) + (size >> page_shift) - 1 )))
    {
        FIXME( "out of memory in virtual heap for %p-%p\n", base, (char *)base + size );
        return STATUS_NO_MEMORY;
    }
476

477
    view->base    = base;
478 479
    view->size    = size;
    view->mapping = 0;
480
    view->protect = vprot;
481
    memset( view->prot, vprot, size >> page_shift );
482

483
    /* Insert it in the linked list */
484

485
    LIST_FOR_EACH( ptr, &views_list )
486
    {
487 488
        struct file_view *next = LIST_ENTRY( ptr, struct file_view, entry );
        if (next->base > base) break;
489
    }
490
    list_add_before( ptr, &view->entry );
491

492 493 494
    /* Check for overlapping views. This can happen if the previous view
     * was a system view that got unmapped behind our back. In that case
     * we recover by simply deleting it. */
495

496
    if ((ptr = list_prev( &views_list, &view->entry )) != NULL)
497
    {
498
        struct file_view *prev = LIST_ENTRY( ptr, struct file_view, entry );
499 500 501 502 503
        if ((char *)prev->base + prev->size > (char *)base)
        {
            TRACE( "overlapping prev view %p-%p for %p-%p\n",
                   prev->base, (char *)prev->base + prev->size,
                   base, (char *)base + view->size );
504
            assert( prev->protect & VPROT_SYSTEM );
505
            delete_view( prev );
506 507
        }
    }
508
    if ((ptr = list_next( &views_list, &view->entry )) != NULL)
509
    {
510 511 512 513 514 515
        struct file_view *next = LIST_ENTRY( ptr, struct file_view, entry );
        if ((char *)base + view->size > (char *)next->base)
        {
            TRACE( "overlapping next view %p-%p for %p-%p\n",
                   next->base, (char *)next->base + next->size,
                   base, (char *)base + view->size );
516
            assert( next->protect & VPROT_SYSTEM );
517
            delete_view( next );
518
        }
519
    }
520 521

    *view_ret = view;
522 523
    VIRTUAL_DEBUG_DUMP_VIEW( view );

524
    if (force_exec_prot && !(vprot & VPROT_NOEXEC) && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
525
    {
526 527
        TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 );
        mprotect( base, size, unix_prot | PROT_EXEC );
528
    }
529
    return STATUS_SUCCESS;
530 531 532 533 534 535 536 537
}


/***********************************************************************
 *           VIRTUAL_GetWin32Prot
 *
 * Convert page protections to Win32 flags.
 */
538
static DWORD VIRTUAL_GetWin32Prot( BYTE vprot )
539
{
540 541 542 543
    DWORD ret = VIRTUAL_Win32Flags[vprot & 0x0f];
    if (vprot & VPROT_NOCACHE) ret |= PAGE_NOCACHE;
    if (vprot & VPROT_GUARD) ret |= PAGE_GUARD;
    return ret;
544 545 546 547
}


/***********************************************************************
548
 *           get_vprot_flags
549 550 551
 *
 * Build page protections from Win32 flags.
 *
552 553 554
 * PARAMS
 *      protect [I] Win32 protection flags
 *
555 556 557
 * RETURNS
 *	Value of page protection flags
 */
558
static NTSTATUS get_vprot_flags( DWORD protect, unsigned int *vprot )
559 560 561 562
{
    switch(protect & 0xff)
    {
    case PAGE_READONLY:
563
        *vprot = VPROT_READ;
564 565
        break;
    case PAGE_READWRITE:
566
        *vprot = VPROT_READ | VPROT_WRITE;
567 568
        break;
    case PAGE_WRITECOPY:
569
        *vprot = VPROT_READ | VPROT_WRITECOPY;
570 571
        break;
    case PAGE_EXECUTE:
572
        *vprot = VPROT_EXEC;
573 574
        break;
    case PAGE_EXECUTE_READ:
575
        *vprot = VPROT_EXEC | VPROT_READ;
576 577
        break;
    case PAGE_EXECUTE_READWRITE:
578
        *vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE;
579 580
        break;
    case PAGE_EXECUTE_WRITECOPY:
581
        *vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITECOPY;
582 583
        break;
    case PAGE_NOACCESS:
584
        *vprot = 0;
585
        break;
586 587
    default:
        return STATUS_INVALID_PARAMETER;
588
    }
589 590 591
    if (protect & PAGE_GUARD) *vprot |= VPROT_GUARD;
    if (protect & PAGE_NOCACHE) *vprot |= VPROT_NOCACHE;
    return STATUS_SUCCESS;
592 593 594 595 596 597 598 599 600 601 602 603
}


/***********************************************************************
 *           VIRTUAL_SetProt
 *
 * Change the protection of a range of pages.
 *
 * RETURNS
 *	TRUE: Success
 *	FALSE: Failure
 */
604
static BOOL VIRTUAL_SetProt( struct file_view *view, /* [in] Pointer to view */
605
                             void *base,      /* [in] Starting address */
606
                             size_t size,     /* [in] Size in bytes */
607 608
                             BYTE vprot )     /* [in] Protections to use */
{
609
    int unix_prot = VIRTUAL_GetUnixProt(vprot);
610
    BYTE *p = view->prot + (((char *)base - (char *)view->base) >> page_shift);
611

612 613 614
    TRACE("%p-%p %s\n",
          base, (char *)base + size - 1, VIRTUAL_GetProtStr( vprot ) );

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
    if (view->protect & VPROT_WRITEWATCH)
    {
        /* each page may need different protections depending on write watch flag */
        UINT i, count;
        char *addr = base;
        int prot;

        p[0] = vprot | (p[0] & VPROT_WRITEWATCH);
        unix_prot = VIRTUAL_GetUnixProt( p[0] );
        for (count = i = 1; i < size >> page_shift; i++, count++)
        {
            p[i] = vprot | (p[i] & VPROT_WRITEWATCH);
            prot = VIRTUAL_GetUnixProt( p[i] );
            if (prot == unix_prot) continue;
            mprotect( addr, count << page_shift, unix_prot );
            addr += count << page_shift;
            unix_prot = prot;
            count = 0;
        }
        if (count) mprotect( addr, count << page_shift, unix_prot );
        VIRTUAL_DEBUG_DUMP_VIEW( view );
        return TRUE;
    }

639 640 641
    /* if setting stack guard pages, store the permissions first, as the guard may be
     * triggered at any point after mprotect and change the permissions again */
    if ((vprot & VPROT_GUARD) &&
642 643
        (base >= NtCurrentTeb()->DeallocationStack) &&
        (base < NtCurrentTeb()->Tib.StackBase))
644
    {
645
        memset( p, vprot, size >> page_shift );
646 647 648 649 650
        mprotect( base, size, unix_prot );
        VIRTUAL_DEBUG_DUMP_VIEW( view );
        return TRUE;
    }

651 652
    if (force_exec_prot && !(view->protect & VPROT_NOEXEC) &&
        (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
653 654 655 656 657 658 659 660
    {
        TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 );
        if (!mprotect( base, size, unix_prot | PROT_EXEC )) goto done;
        /* exec + write may legitimately fail, in that case fall back to write only */
        if (!(unix_prot & PROT_WRITE)) return FALSE;
    }

    if (mprotect( base, size, unix_prot )) return FALSE;  /* FIXME: last error */
661

662
done:
663
    memset( p, vprot, size >> page_shift );
664 665 666 667 668
    VIRTUAL_DEBUG_DUMP_VIEW( view );
    return TRUE;
}


669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
/***********************************************************************
 *           reset_write_watches
 *
 * Reset write watches in a memory range.
 */
static void reset_write_watches( struct file_view *view, void *base, SIZE_T size )
{
    SIZE_T i, count;
    int prot, unix_prot;
    char *addr = base;
    BYTE *p = view->prot + ((addr - (char *)view->base) >> page_shift);

    p[0] |= VPROT_WRITEWATCH;
    unix_prot = VIRTUAL_GetUnixProt( p[0] );
    for (count = i = 1; i < size >> page_shift; i++, count++)
    {
        p[i] |= VPROT_WRITEWATCH;
        prot = VIRTUAL_GetUnixProt( p[i] );
        if (prot == unix_prot) continue;
        mprotect( addr, count << page_shift, unix_prot );
        addr += count << page_shift;
        unix_prot = prot;
        count = 0;
    }
    if (count) mprotect( addr, count << page_shift, unix_prot );
}


697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
/***********************************************************************
 *           unmap_extra_space
 *
 * Release the extra memory while keeping the range starting on the granularity boundary.
 */
static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t wanted_size, size_t mask )
{
    if ((ULONG_PTR)ptr & mask)
    {
        size_t extra = mask + 1 - ((ULONG_PTR)ptr & mask);
        munmap( ptr, extra );
        ptr = (char *)ptr + extra;
        total_size -= extra;
    }
    if (total_size > wanted_size)
        munmap( (char *)ptr + wanted_size, total_size - wanted_size );
    return ptr;
}


717 718 719 720 721
struct alloc_area
{
    size_t size;
    size_t mask;
    int    top_down;
722
    void  *limit;
723 724 725 726 727 728 729 730 731 732 733 734 735 736
    void  *result;
};

/***********************************************************************
 *           alloc_reserved_area_callback
 *
 * Try to map some space inside a reserved area. Callback for wine_mmap_enum_reserved_areas.
 */
static int alloc_reserved_area_callback( void *start, size_t size, void *arg )
{
    struct alloc_area *alloc = arg;
    void *end = (char *)start + size;

    if (start < address_space_start) start = address_space_start;
737
    if (is_beyond_limit( start, size, alloc->limit )) end = alloc->limit;
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
    if (start >= end) return 0;

    /* make sure we don't touch the preloader reserved range */
    if (preload_reserve_end >= start)
    {
        if (preload_reserve_end >= end)
        {
            if (preload_reserve_start <= start) return 0;  /* no space in that area */
            if (preload_reserve_start < end) end = preload_reserve_start;
        }
        else if (preload_reserve_start <= start) start = preload_reserve_end;
        else
        {
            /* range is split in two by the preloader reservation, try first part */
            if ((alloc->result = find_free_area( start, preload_reserve_start, alloc->size,
                                                 alloc->mask, alloc->top_down )))
                return 1;
            /* then fall through to try second part */
            start = preload_reserve_end;
        }
    }
    if ((alloc->result = find_free_area( start, end, alloc->size, alloc->mask, alloc->top_down )))
        return 1;

    return 0;
}


766
/***********************************************************************
767
 *           map_view
768
 *
769 770
 * Create a view and mmap the corresponding memory area.
 * The csVirtual section must be held by caller.
771
 */
772
static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, size_t mask,
773
                          int top_down, unsigned int vprot )
774
{
775 776
    void *ptr;
    NTSTATUS status;
777

778
    if (base)
779
    {
780
        if (is_beyond_limit( base, size, address_space_limit ))
781 782 783
            return STATUS_WORKING_SET_LIMIT_RANGE;

        switch (wine_mmap_is_in_reserved_area( base, size ))
784
        {
785
        case -1: /* partially in a reserved area */
786
            return STATUS_CONFLICTING_ADDRESSES;
787 788 789 790 791 792 793 794 795 796

        case 0:  /* not in a reserved area, do a normal allocation */
            if ((ptr = wine_anon_mmap( base, size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1)
            {
                if (errno == ENOMEM) return STATUS_NO_MEMORY;
                return STATUS_INVALID_PARAMETER;
            }
            if (ptr != base)
            {
                /* We couldn't get the address we wanted */
797
                if (is_beyond_limit( ptr, size, user_space_limit )) add_reserved_area( ptr, size );
798 799 800 801 802 803 804 805 806 807 808 809
                else munmap( ptr, size );
                return STATUS_CONFLICTING_ADDRESSES;
            }
            break;

        default:
        case 1:  /* in a reserved area, make sure the address is available */
            if (find_view_range( base, size )) return STATUS_CONFLICTING_ADDRESSES;
            /* replace the reserved area by our mapping */
            if ((ptr = wine_anon_mmap( base, size, VIRTUAL_GetUnixProt(vprot), MAP_FIXED )) != base)
                return STATUS_INVALID_PARAMETER;
            break;
810
        }
811
        if (is_beyond_limit( ptr, size, working_set_limit )) working_set_limit = address_space_limit;
812
    }
813
    else
814
    {
815
        size_t view_size = size + mask + 1;
816 817 818 819 820
        struct alloc_area alloc;

        alloc.size = size;
        alloc.mask = mask;
        alloc.top_down = top_down;
821
        alloc.limit = user_space_limit;
822 823 824 825 826 827 828 829
        if (wine_mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down ))
        {
            ptr = alloc.result;
            TRACE( "got mem in reserved area %p-%p\n", ptr, (char *)ptr + size );
            if (wine_anon_mmap( ptr, size, VIRTUAL_GetUnixProt(vprot), MAP_FIXED ) != ptr)
                return STATUS_INVALID_PARAMETER;
            goto done;
        }
830

831
        for (;;)
832
        {
833 834 835 836 837
            if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1)
            {
                if (errno == ENOMEM) return STATUS_NO_MEMORY;
                return STATUS_INVALID_PARAMETER;
            }
838
            TRACE( "got mem with anon mmap %p-%p\n", ptr, (char *)ptr + size );
839
            /* if we got something beyond the user limit, unmap it and retry */
840
            if (is_beyond_limit( ptr, view_size, user_space_limit )) add_reserved_area( ptr, view_size );
841
            else break;
842
        }
843
        ptr = unmap_extra_space( ptr, view_size, size, mask );
844
    }
845
done:
846
    status = create_view( view_ret, ptr, size, vprot );
847
    if (status != STATUS_SUCCESS) unmap_area( ptr, size );
848 849 850 851 852 853 854 855 856 857 858
    return status;
}


/***********************************************************************
 *           map_file_into_view
 *
 * Wrapper for mmap() to map a file into a view, falling back to read if mmap fails.
 * The csVirtual section must be held by caller.
 */
static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start, size_t size,
859
                                    off_t offset, unsigned int vprot, BOOL removable )
860 861
{
    void *ptr;
862
    int prot = VIRTUAL_GetUnixProt( vprot | VPROT_COMMITTED /* make sure it is accessible */ );
863 864 865 866 867
    BOOL shared_write = (vprot & VPROT_WRITE) != 0;

    assert( start < view->size );
    assert( start + size <= view->size );

868 869 870 871 872 873 874
    if (force_exec_prot && !(vprot & VPROT_NOEXEC) && (vprot & VPROT_READ))
    {
        TRACE( "forcing exec permission on mapping %p-%p\n",
               (char *)view->base + start, (char *)view->base + start + size - 1 );
        prot |= PROT_EXEC;
    }

875 876
    /* only try mmap if media is not removable (or if we require write access) */
    if (!removable || shared_write)
877
    {
878 879
        int flags = MAP_FIXED | (shared_write ? MAP_SHARED : MAP_PRIVATE);

880
        if (mmap( (char *)view->base + start, size, prot, flags, fd, offset ) != (void *)-1)
881 882
            goto done;

883 884 885
        if ((errno == EPERM) && (prot & PROT_EXEC))
            ERR( "failed to set %08x protection on file map, noexec filesystem?\n", prot );

886 887 888 889
        /* mmap() failed; if this is because the file offset is not    */
        /* page-aligned (EINVAL), or because the underlying filesystem */
        /* does not support mmap() (ENOEXEC,ENODEV), we do it by hand. */
        if ((errno != ENOEXEC) && (errno != EINVAL) && (errno != ENODEV)) return FILE_GetNtStatus();
890 891 892 893 894 895
        if (shared_write)  /* we cannot fake shared write mappings */
        {
            if (errno == EINVAL) return STATUS_INVALID_PARAMETER;
            ERR( "shared writable mmap not supported, broken filesystem?\n" );
            return STATUS_NOT_SUPPORTED;
        }
896
    }
897 898 899 900 901 902 903 904

    /* Reserve the memory with an anonymous mmap */
    ptr = wine_anon_mmap( (char *)view->base + start, size, PROT_READ | PROT_WRITE, MAP_FIXED );
    if (ptr == (void *)-1) return FILE_GetNtStatus();
    /* Now read in the file */
    pread( fd, ptr, size, offset );
    if (prot != (PROT_READ|PROT_WRITE)) mprotect( ptr, size, prot );  /* Set the right protection */
done:
905
    memset( view->prot + (start >> page_shift), vprot, ROUND_SIZE(start,size) >> page_shift );
906 907 908 909
    return STATUS_SUCCESS;
}


910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
/***********************************************************************
 *           get_committed_size
 *
 * Get the size of the committed range starting at base.
 * Also return the protections for the first page.
 */
static SIZE_T get_committed_size( struct file_view *view, void *base, BYTE *vprot )
{
    SIZE_T i, start;

    start = ((char *)base - (char *)view->base) >> page_shift;
    *vprot = view->prot[start];

    if (view->mapping && !(view->protect & VPROT_COMMITTED))
    {
        SIZE_T ret = 0;
        SERVER_START_REQ( get_mapping_committed_range )
        {
928
            req->handle = wine_server_obj_handle( view->mapping );
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
            req->offset = start << page_shift;
            if (!wine_server_call( req ))
            {
                ret = reply->size;
                if (reply->committed)
                {
                    *vprot |= VPROT_COMMITTED;
                    for (i = 0; i < ret >> page_shift; i++) view->prot[start+i] |= VPROT_COMMITTED;
                }
            }
        }
        SERVER_END_REQ;
        return ret;
    }
    for (i = start + 1; i < view->size >> page_shift; i++)
        if ((*vprot ^ view->prot[i]) & VPROT_COMMITTED) break;
    return (i - start) << page_shift;
}


949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
/***********************************************************************
 *           decommit_view
 *
 * Decommit some pages of a given view.
 * The csVirtual section must be held by caller.
 */
static NTSTATUS decommit_pages( struct file_view *view, size_t start, size_t size )
{
    if (wine_anon_mmap( (char *)view->base + start, size, PROT_NONE, MAP_FIXED ) != (void *)-1)
    {
        BYTE *p = view->prot + (start >> page_shift);
        size >>= page_shift;
        while (size--) *p++ &= ~VPROT_COMMITTED;
        return STATUS_SUCCESS;
    }
    return FILE_GetNtStatus();
}


968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
/***********************************************************************
 *           allocate_dos_memory
 *
 * Allocate the DOS memory range.
 */
static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot )
{
    size_t size;
    void *addr = NULL;
    void * const low_64k = (void *)0x10000;
    const size_t dosmem_size = 0x110000;
    int unix_prot = VIRTUAL_GetUnixProt( vprot );
    struct list *ptr;

    /* check for existing view */

    if ((ptr = list_head( &views_list )))
    {
        struct file_view *first_view = LIST_ENTRY( ptr, struct file_view, entry );
        if (first_view->base < (void *)dosmem_size) return STATUS_CONFLICTING_ADDRESSES;
    }

    /* check without the first 64K */

    if (wine_mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1)
    {
        addr = wine_anon_mmap( low_64k, dosmem_size - 0x10000, unix_prot, 0 );
        if (addr != low_64k)
        {
            if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 );
            return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot );
        }
    }

    /* now try to allocate the low 64K too */

    if (wine_mmap_is_in_reserved_area( NULL, 0x10000 ) != 1)
    {
        addr = wine_anon_mmap( (void *)page_size, 0x10000 - page_size, unix_prot, 0 );
        if (addr == (void *)page_size)
        {
1009 1010 1011 1012 1013 1014
            if (!wine_anon_mmap( NULL, page_size, unix_prot, MAP_FIXED ))
            {
                addr = NULL;
                TRACE( "successfully mapped low 64K range\n" );
            }
            else TRACE( "failed to map page 0\n" );
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
        }
        else
        {
            if (addr != (void *)-1) munmap( addr, 0x10000 - page_size );
            addr = low_64k;
            TRACE( "failed to map low 64K range\n" );
        }
    }

    /* now reserve the whole range */

    size = (char *)dosmem_size - (char *)addr;
    wine_anon_mmap( addr, size, unix_prot, MAP_FIXED );
    return create_view( view, addr, size, vprot );
}


1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
/***********************************************************************
 *           check_architecture
 *
 * Check the architecture of a PE binary.
 */
static NTSTATUS check_architecture( const IMAGE_NT_HEADERS *nt )
{
    static const char *arch;

#ifdef __i386__
    if (nt->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) return STATUS_SUCCESS;
    if (nt->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
    {
        if (nt->FileHeader.Characteristics & IMAGE_FILE_DLL)  /* don't warn for a 64-bit exe */
            WARN( "loading amd64 dll in 32-bit mode will fail\n" );
        return STATUS_INVALID_IMAGE_FORMAT;
    }
#elif defined(__x86_64__)
    if (nt->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) return STATUS_SUCCESS;
    if (nt->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
    {
        if (nt->FileHeader.Characteristics & IMAGE_FILE_DLL)  /* don't warn for a 32-bit exe */
            WARN( "loading 32-bit dll in 64-bit mode will fail\n" );
        return STATUS_INVALID_IMAGE_FORMAT;
    }
1057
#elif defined(__arm__) && !defined(__ARMEB__)
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
    if (nt->FileHeader.Machine == IMAGE_FILE_MACHINE_ARM ||
        nt->FileHeader.Machine == IMAGE_FILE_MACHINE_THUMB)
        return STATUS_SUCCESS;
#endif

    switch (nt->FileHeader.Machine)
    {
        case IMAGE_FILE_MACHINE_UNKNOWN: arch = "Unknown"; break;
        case IMAGE_FILE_MACHINE_I860:    arch = "I860"; break;
        case IMAGE_FILE_MACHINE_I386:    arch = "I386"; break;
        case IMAGE_FILE_MACHINE_R3000:   arch = "R3000"; break;
        case IMAGE_FILE_MACHINE_R4000:   arch = "R4000"; break;
        case IMAGE_FILE_MACHINE_R10000:  arch = "R10000"; break;
        case IMAGE_FILE_MACHINE_ALPHA:   arch = "Alpha"; break;
        case IMAGE_FILE_MACHINE_POWERPC: arch = "PowerPC"; break;
        case IMAGE_FILE_MACHINE_IA64:    arch = "IA-64"; break;
        case IMAGE_FILE_MACHINE_ALPHA64: arch = "Alpha-64"; break;
        case IMAGE_FILE_MACHINE_AMD64:   arch = "AMD-64"; break;
        case IMAGE_FILE_MACHINE_ARM:     arch = "ARM"; break;
        case IMAGE_FILE_MACHINE_THUMB:   arch = "ARM Thumb"; break;
        case IMAGE_FILE_MACHINE_SPARC:   arch = "SPARC"; break;
        default: arch = wine_dbg_sprintf( "Unknown-%04x", nt->FileHeader.Machine ); break;
    }
    ERR( "Trying to load PE image for unsupported architecture %s\n", arch );
    return STATUS_INVALID_IMAGE_FORMAT;
}


1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
/***********************************************************************
 *           stat_mapping_file
 *
 * Stat the underlying file for a memory view.
 */
static NTSTATUS stat_mapping_file( struct file_view *view, struct stat *st )
{
    NTSTATUS status;
    int unix_fd, needs_close;

    if (!view->mapping) return STATUS_NOT_MAPPED_VIEW;
    if (!(status = server_get_unix_fd( view->mapping, 0, &unix_fd, &needs_close, NULL, NULL )))
    {
        if (fstat( unix_fd, st ) == -1) status = FILE_GetNtStatus();
        if (needs_close) close( unix_fd );
    }
    return status;
}


1106 1107 1108 1109 1110
/***********************************************************************
 *           map_image
 *
 * Map an executable (PE format) image into memory.
 */
1111
static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_size, SIZE_T mask,
1112
                           SIZE_T header_size, int shared_fd, HANDLE dup_mapping, PVOID *addr_ptr )
1113 1114 1115 1116 1117
{
    IMAGE_DOS_HEADER *dos;
    IMAGE_NT_HEADERS *nt;
    IMAGE_SECTION_HEADER *sec;
    IMAGE_DATA_DIRECTORY *imports;
1118 1119 1120
    NTSTATUS status = STATUS_CONFLICTING_ADDRESSES;
    int i;
    off_t pos;
1121
    sigset_t sigset;
1122
    struct stat st;
1123
    struct file_view *view = NULL;
1124
    char *ptr, *header_end;
1125
    INT_PTR delta = 0;
1126 1127 1128

    /* zero-map the whole range */

1129
    server_enter_uninterrupted_section( &csVirtual, &sigset );
1130

1131
    if (base >= (char *)address_space_start)  /* make sure the DOS area remains free */
1132
        status = map_view( &view, base, total_size, mask, FALSE,
1133 1134
                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY | VPROT_IMAGE );

1135
    if (status != STATUS_SUCCESS)
1136
        status = map_view( &view, NULL, total_size, mask, FALSE,
1137 1138 1139 1140 1141
                           VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY | VPROT_IMAGE );

    if (status != STATUS_SUCCESS) goto error;

    ptr = view->base;
1142 1143 1144 1145
    TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );

    /* map the header */

1146 1147 1148 1149 1150
    if (fstat( fd, &st ) == -1)
    {
        status = FILE_GetNtStatus();
        goto error;
    }
1151
    status = STATUS_INVALID_IMAGE_FORMAT;  /* generic error */
1152 1153
    if (!st.st_size) goto error;
    header_size = min( header_size, st.st_size );
1154
    if (map_file_into_view( view, fd, 0, header_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
1155
                            !dup_mapping ) != STATUS_SUCCESS) goto error;
1156 1157
    dos = (IMAGE_DOS_HEADER *)ptr;
    nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
1158
    header_end = ptr + ROUND_SIZE( 0, header_size );
1159
    memset( ptr + header_size, 0, header_end - (ptr + header_size) );
1160
    if ((char *)(nt + 1) > header_end) goto error;
1161
    sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
1162
    if ((char *)(sec + nt->FileHeader.NumberOfSections) > header_end) goto error;
1163 1164 1165 1166

    imports = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
    if (!imports->Size || !imports->VirtualAddress) imports = NULL;

1167
    if (check_architecture( nt )) goto error;
1168

1169 1170 1171 1172 1173 1174 1175 1176
    /* check for non page-aligned binary */

    if (nt->OptionalHeader.SectionAlignment <= page_mask)
    {
        /* unaligned sections, this happens for native subsystem binaries */
        /* in that case Windows simply maps in the whole file */

        if (map_file_into_view( view, fd, 0, total_size, 0, VPROT_COMMITTED | VPROT_READ,
1177
                                !dup_mapping ) != STATUS_SUCCESS) goto error;
1178 1179

        /* check that all sections are loaded at the right offset */
1180
        if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto error;
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
        for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
        {
            if (sec[i].VirtualAddress != sec[i].PointerToRawData)
                goto error;  /* Windows refuses to load in that case too */
        }

        /* set the image protections */
        VIRTUAL_SetProt( view, ptr, total_size,
                         VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );

1191
        /* no relocations are performed on non page-aligned binaries */
1192 1193 1194 1195
        goto done;
    }


1196 1197 1198 1199
    /* map all the sections */

    for (i = pos = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
    {
1200 1201
        static const SIZE_T sector_align = 0x1ff;
        SIZE_T map_size, file_start, file_size, end;
1202 1203

        if (!sec->Misc.VirtualSize)
1204
            map_size = ROUND_SIZE( 0, sec->SizeOfRawData );
1205 1206
        else
            map_size = ROUND_SIZE( 0, sec->Misc.VirtualSize );
1207 1208 1209 1210 1211

        /* file positions are rounded to sector boundaries regardless of OptionalHeader.FileAlignment */
        file_start = sec->PointerToRawData & ~sector_align;
        file_size = (sec->SizeOfRawData + (sec->PointerToRawData & sector_align) + sector_align) & ~sector_align;
        if (file_size > map_size) file_size = map_size;
1212 1213

        /* a few sanity checks */
1214 1215
        end = sec->VirtualAddress + ROUND_SIZE( sec->VirtualAddress, map_size );
        if (sec->VirtualAddress > total_size || end > total_size || end < sec->VirtualAddress)
1216
        {
1217 1218
            WARN_(module)( "Section %.8s too large (%x+%lx/%lx)\n",
                           sec->Name, sec->VirtualAddress, map_size, total_size );
1219 1220 1221 1222 1223 1224
            goto error;
        }

        if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) &&
            (sec->Characteristics & IMAGE_SCN_MEM_WRITE))
        {
1225
            TRACE_(module)( "mapping shared section %.8s at %p off %x (%x) size %lx (%lx) flags %x\n",
1226 1227 1228 1229
                            sec->Name, ptr + sec->VirtualAddress,
                            sec->PointerToRawData, (int)pos, file_size, map_size,
                            sec->Characteristics );
            if (map_file_into_view( view, shared_fd, sec->VirtualAddress, map_size, pos,
1230
                                    VPROT_COMMITTED | VPROT_READ | VPROT_WRITE,
1231
                                    FALSE ) != STATUS_SUCCESS)
1232 1233 1234 1235 1236 1237 1238
            {
                ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
                goto error;
            }

            /* check if the import directory falls inside this section */
            if (imports && imports->VirtualAddress >= sec->VirtualAddress &&
1239
                imports->VirtualAddress < sec->VirtualAddress + map_size)
1240 1241 1242
            {
                UINT_PTR base = imports->VirtualAddress & ~page_mask;
                UINT_PTR end = base + ROUND_SIZE( imports->VirtualAddress, imports->Size );
1243
                if (end > sec->VirtualAddress + map_size) end = sec->VirtualAddress + map_size;
1244 1245 1246 1247 1248
                if (end > base)
                    map_file_into_view( view, shared_fd, base, end - base,
                                        pos + (base - sec->VirtualAddress),
                                        VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
                                        FALSE );
1249
            }
1250
            pos += map_size;
1251 1252 1253
            continue;
        }

1254
        TRACE_(module)( "mapping section %.8s at %p off %x size %x virt %x flags %x\n",
1255 1256
                        sec->Name, ptr + sec->VirtualAddress,
                        sec->PointerToRawData, sec->SizeOfRawData,
1257
                        sec->Misc.VirtualSize, sec->Characteristics );
1258

1259
        if (!sec->PointerToRawData || !file_size) continue;
1260

1261
        /* Note: if the section is not aligned properly map_file_into_view will magically
1262 1263
         *       fall back to read(), so we don't need to check anything here.
         */
1264 1265 1266 1267 1268
        end = file_start + file_size;
        if (sec->PointerToRawData >= st.st_size ||
            end > ((st.st_size + sector_align) & ~sector_align) ||
            end < file_start ||
            map_file_into_view( view, fd, sec->VirtualAddress, file_size, file_start,
1269
                                VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
1270
                                !dup_mapping ) != STATUS_SUCCESS)
1271 1272 1273 1274 1275
        {
            ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
            goto error;
        }

1276
        if (file_size & page_mask)
1277
        {
1278 1279
            end = ROUND_SIZE( 0, file_size );
            if (end > map_size) end = map_size;
1280
            TRACE_(module)("clearing %p - %p\n",
1281
                           ptr + sec->VirtualAddress + file_size,
1282
                           ptr + sec->VirtualAddress + end );
1283
            memset( ptr + sec->VirtualAddress + file_size, 0, end - file_size );
1284 1285 1286 1287 1288 1289
        }
    }


    /* perform base relocation, if necessary */

1290 1291 1292
    if (ptr != base &&
        ((nt->FileHeader.Characteristics & IMAGE_FILE_DLL) ||
          !NtCurrentTeb()->Peb->ImageBaseAddress) )
1293
    {
1294
        IMAGE_BASE_RELOCATION *rel, *end;
1295 1296
        const IMAGE_DATA_DIRECTORY *relocs;

1297
        if (nt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
1298
        {
1299 1300
            WARN_(module)( "Need to relocate module from %p to %p, but there are no relocation records\n",
                           base, ptr );
1301
            status = STATUS_CONFLICTING_ADDRESSES;
1302 1303 1304
            goto error;
        }

1305 1306 1307 1308 1309 1310
        TRACE_(module)( "relocating from %p-%p to %p-%p\n",
                        base, base + total_size, ptr, ptr + total_size );

        relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
        rel = (IMAGE_BASE_RELOCATION *)(ptr + relocs->VirtualAddress);
        end = (IMAGE_BASE_RELOCATION *)(ptr + relocs->VirtualAddress + relocs->Size);
1311
        delta = ptr - base;
1312

1313
        while (rel < end - 1 && rel->SizeOfBlock)
1314
        {
1315 1316 1317 1318 1319 1320
            if (rel->VirtualAddress >= total_size)
            {
                WARN_(module)( "invalid address %p in relocation %p\n", ptr + rel->VirtualAddress, rel );
                status = STATUS_ACCESS_VIOLATION;
                goto error;
            }
1321 1322 1323 1324
            rel = LdrProcessRelocationBlock( ptr + rel->VirtualAddress,
                                             (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
                                             (USHORT *)(rel + 1), delta );
            if (!rel) goto error;
1325 1326 1327 1328 1329
        }
    }

    /* set the image protections */

1330 1331
    VIRTUAL_SetProt( view, ptr, ROUND_SIZE( 0, header_size ), VPROT_COMMITTED | VPROT_READ );

1332 1333 1334
    sec = (IMAGE_SECTION_HEADER*)((char *)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
    for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
    {
1335
        SIZE_T size;
1336
        BYTE vprot = VPROT_COMMITTED;
1337 1338 1339 1340 1341 1342

        if (sec->Misc.VirtualSize)
            size = ROUND_SIZE( sec->VirtualAddress, sec->Misc.VirtualSize );
        else
            size = ROUND_SIZE( sec->VirtualAddress, sec->SizeOfRawData );

1343 1344
        if (sec->Characteristics & IMAGE_SCN_MEM_READ)    vprot |= VPROT_READ;
        if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) vprot |= VPROT_EXEC;
1345 1346 1347 1348 1349 1350 1351
        if (sec->Characteristics & IMAGE_SCN_MEM_WRITE)
        {
            if (sec->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
                vprot |= VPROT_READ | VPROT_WRITE;
            else
                vprot |= VPROT_READ | VPROT_WRITECOPY;
        }
1352 1353 1354 1355 1356 1357

        /* Dumb game crack lets the AOEP point into a data section. Adjust. */
        if ((nt->OptionalHeader.AddressOfEntryPoint >= sec->VirtualAddress) &&
            (nt->OptionalHeader.AddressOfEntryPoint < sec->VirtualAddress + size))
            vprot |= VPROT_EXEC;

1358 1359 1360
        if (!VIRTUAL_SetProt( view, ptr + sec->VirtualAddress, size, vprot ) && (vprot & VPROT_EXEC))
            ERR( "failed to set %08x protection on section %.8s, noexec filesystem?\n",
                 sec->Characteristics, sec->Name );
1361
    }
1362 1363

 done:
1364
    view->mapping = dup_mapping;
1365
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1366 1367

    *addr_ptr = ptr;
1368 1369 1370
#ifdef VALGRIND_LOAD_PDB_DEBUGINFO
    VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta);
#endif
1371
    if (ptr != base) return STATUS_IMAGE_NOT_AT_BASE;
1372 1373 1374
    return STATUS_SUCCESS;

 error:
1375
    if (view) delete_view( view );
1376
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1377
    if (dup_mapping) NtClose( dup_mapping );
1378 1379 1380 1381
    return status;
}


1382 1383
/* callback for wine_mmap_enum_reserved_areas to allocate space for the virtual heap */
static int alloc_virtual_heap( void *base, size_t size, void *arg )
1384
{
1385 1386
    void **heap_base = arg;

1387
    if (is_beyond_limit( base, size, address_space_limit )) address_space_limit = (char *)base + size;
1388
    if (size < VIRTUAL_HEAP_SIZE) return 0;
1389
    if (is_win64 && base < (void *)0x80000000) return 0;
1390 1391 1392
    *heap_base = wine_anon_mmap( (char *)base + size - VIRTUAL_HEAP_SIZE,
                                 VIRTUAL_HEAP_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED );
    return (*heap_base != (void *)-1);
1393 1394
}

1395
/***********************************************************************
1396
 *           virtual_init
1397
 */
1398
void virtual_init(void)
1399
{
1400
    const char *preload;
1401
    void *heap_base;
1402
    size_t size;
1403 1404
    struct file_view *heap_view;

1405
#ifndef page_mask
1406 1407 1408 1409 1410 1411
    page_size = getpagesize();
    page_mask = page_size - 1;
    /* Make sure we have a power of 2 */
    assert( !(page_size & page_mask) );
    page_shift = 0;
    while ((1 << page_shift) != page_size) page_shift++;
1412
    user_space_limit = working_set_limit = address_space_limit = (void *)~page_mask;
1413
#endif  /* page_mask */
1414 1415 1416 1417 1418 1419 1420 1421 1422
    if ((preload = getenv("WINEPRELOADRESERVE")))
    {
        unsigned long start, end;
        if (sscanf( preload, "%lx-%lx", &start, &end ) == 2)
        {
            preload_reserve_start = (void *)start;
            preload_reserve_end = (void *)end;
        }
    }
1423 1424 1425 1426 1427 1428 1429 1430 1431

    /* try to find space in a reserved area for the virtual heap */
    if (!wine_mmap_enum_reserved_areas( alloc_virtual_heap, &heap_base, 1 ))
        heap_base = wine_anon_mmap( NULL, VIRTUAL_HEAP_SIZE, PROT_READ|PROT_WRITE, 0 );

    assert( heap_base != (void *)-1 );
    virtual_heap = RtlCreateHeap( HEAP_NO_SERIALIZE, heap_base, VIRTUAL_HEAP_SIZE,
                                  VIRTUAL_HEAP_SIZE, NULL, NULL );
    create_view( &heap_view, heap_base, VIRTUAL_HEAP_SIZE, VPROT_COMMITTED | VPROT_READ | VPROT_WRITE );
1432

1433 1434 1435 1436
    /* make the DOS area accessible (except the low 64K) to hide bugs in broken apps like Excel 2003 */
    size = (char *)address_space_start - (char *)0x10000;
    if (size && wine_mmap_is_in_reserved_area( (void*)0x10000, size ) == 1)
        wine_anon_mmap( (void *)0x10000, size, PROT_READ | PROT_WRITE, MAP_FIXED );
1437
}
1438 1439


1440
/***********************************************************************
1441
 *           virtual_init_threading
1442
 */
1443
void virtual_init_threading(void)
1444
{
1445
    use_locks = 1;
1446 1447 1448
}


1449 1450 1451 1452 1453
/***********************************************************************
 *           virtual_get_system_info
 */
void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info )
{
1454 1455 1456 1457 1458 1459 1460 1461 1462
    info->unknown                 = 0;
    info->KeMaximumIncrement      = 0;  /* FIXME */
    info->PageSize                = page_size;
    info->MmLowestPhysicalPage    = 1;
    info->MmHighestPhysicalPage   = 0x7fffffff / page_size;
    info->MmNumberOfPhysicalPages = info->MmHighestPhysicalPage - info->MmLowestPhysicalPage;
    info->AllocationGranularity   = get_mask(0) + 1;
    info->LowestUserAddress       = (void *)0x10000;
    info->HighestUserAddress      = (char *)user_space_limit - 1;
1463
    info->ActiveProcessorsAffinityMask = (1 << NtCurrentTeb()->Peb->NumberOfProcessors) - 1;
1464
    info->NumberOfProcessors      = NtCurrentTeb()->Peb->NumberOfProcessors;
1465 1466 1467
}


1468
/***********************************************************************
André Hentschel's avatar
André Hentschel committed
1469
 *           virtual_create_builtin_view
1470
 */
1471
NTSTATUS virtual_create_builtin_view( void *module )
1472 1473 1474
{
    NTSTATUS status;
    sigset_t sigset;
1475 1476 1477
    IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
    SIZE_T size = nt->OptionalHeader.SizeOfImage;
    IMAGE_SECTION_HEADER *sec;
1478
    struct file_view *view;
1479 1480
    void *base;
    int i;
1481

1482 1483
    size = ROUND_SIZE( module, size );
    base = ROUND_ADDR( module, page_mask );
1484
    server_enter_uninterrupted_section( &csVirtual, &sigset );
1485 1486
    status = create_view( &view, base, size, VPROT_SYSTEM | VPROT_IMAGE |
                          VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );
1487 1488
    if (!status) TRACE( "created %p-%p\n", base, (char *)base + size );
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1489 1490 1491

    if (status) return status;

1492 1493 1494
    /* The PE header is always read-only, no write, no execute. */
    view->prot[0] = VPROT_COMMITTED | VPROT_READ;

1495 1496 1497
    sec = (IMAGE_SECTION_HEADER *)((char *)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader);
    for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
    {
1498
        BYTE flags = VPROT_COMMITTED;
1499 1500 1501 1502 1503 1504 1505 1506

        if (sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) flags |= VPROT_EXEC;
        if (sec[i].Characteristics & IMAGE_SCN_MEM_READ) flags |= VPROT_READ;
        if (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE) flags |= VPROT_WRITE;
        memset (view->prot + (sec[i].VirtualAddress >> page_shift), flags,
                ROUND_SIZE( sec[i].VirtualAddress, sec[i].Misc.VirtualSize ) >> page_shift );
    }

1507 1508 1509 1510
    return status;
}


1511 1512 1513
/***********************************************************************
 *           virtual_alloc_thread_stack
 */
1514
NTSTATUS virtual_alloc_thread_stack( TEB *teb, SIZE_T reserve_size, SIZE_T commit_size )
1515
{
1516
    struct file_view *view;
1517 1518
    NTSTATUS status;
    sigset_t sigset;
1519
    SIZE_T size;
1520

1521
    if (!reserve_size || !commit_size)
1522
    {
1523 1524 1525
        IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
        if (!reserve_size) reserve_size = nt->OptionalHeader.SizeOfStackReserve;
        if (!commit_size) commit_size = nt->OptionalHeader.SizeOfStackCommit;
1526
    }
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537

    size = max( reserve_size, commit_size );
    if (size < 1024 * 1024) size = 1024 * 1024;  /* Xlib needs a large stack */
    size = (size + 0xffff) & ~0xffff;  /* round to 64K boundary */

    server_enter_uninterrupted_section( &csVirtual, &sigset );

    if ((status = map_view( &view, NULL, size, 0xffff, 0,
                            VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_VALLOC )) != STATUS_SUCCESS)
        goto done;

1538
#ifdef VALGRIND_STACK_REGISTER
1539
    VALGRIND_STACK_REGISTER( view->base, (char *)view->base + view->size );
1540 1541 1542 1543
#endif

    /* setup no access guard page */
    VIRTUAL_SetProt( view, view->base, page_size, VPROT_COMMITTED );
1544 1545
    VIRTUAL_SetProt( view, (char *)view->base + page_size, page_size,
                     VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD );
1546 1547

    /* note: limit is lower than base since the stack grows down */
1548 1549 1550
    teb->DeallocationStack = view->base;
    teb->Tib.StackBase     = (char *)view->base + view->size;
    teb->Tib.StackLimit    = (char *)view->base + 2 * page_size;
1551 1552 1553 1554 1555 1556
done:
    server_leave_uninterrupted_section( &csVirtual, &sigset );
    return status;
}


1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
/***********************************************************************
 *           virtual_clear_thread_stack
 *
 * Clear the stack contents before calling the main entry point, some broken apps need that.
 */
void virtual_clear_thread_stack(void)
{
    void *stack = NtCurrentTeb()->Tib.StackLimit;
    size_t size = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;

    wine_anon_mmap( stack, size, PROT_READ | PROT_WRITE, MAP_FIXED );
    if (force_exec_prot) mprotect( stack, size, PROT_READ | PROT_WRITE | PROT_EXEC );
}


1572
/***********************************************************************
1573
 *           virtual_handle_fault
1574
 */
1575
NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err )
1576
{
1577
    struct file_view *view;
1578
    NTSTATUS ret = STATUS_ACCESS_VIOLATION;
1579
    sigset_t sigset;
1580

1581
    server_enter_uninterrupted_section( &csVirtual, &sigset );
1582
    if ((view = VIRTUAL_FindView( addr, 0 )))
1583
    {
1584
        void *page = ROUND_ADDR( addr, page_mask );
1585 1586
        BYTE *vprot = &view->prot[((const char *)page - (const char *)view->base) >> page_shift];
        if (*vprot & VPROT_GUARD)
1587
        {
1588
            VIRTUAL_SetProt( view, page, page_size, *vprot & ~VPROT_GUARD );
1589
            ret = STATUS_GUARD_PAGE_VIOLATION;
1590
        }
1591
        if ((err & EXCEPTION_WRITE_FAULT) && (view->protect & VPROT_WRITEWATCH))
1592
        {
1593 1594 1595 1596 1597 1598 1599
            if (*vprot & VPROT_WRITEWATCH)
            {
                *vprot &= ~VPROT_WRITEWATCH;
                VIRTUAL_SetProt( view, page, page_size, *vprot );
            }
            /* ignore fault if page is writable now */
            if (VIRTUAL_GetUnixProt( *vprot ) & PROT_WRITE) ret = STATUS_SUCCESS;
1600
        }
1601
    }
1602
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1603 1604 1605 1606
    return ret;
}


1607 1608 1609 1610 1611 1612 1613 1614 1615

/***********************************************************************
 *           virtual_handle_stack_fault
 *
 * Handle an access fault inside the current thread stack.
 * Called from inside a signal handler.
 */
BOOL virtual_handle_stack_fault( void *addr )
{
1616
    struct file_view *view;
1617 1618 1619
    BOOL ret = FALSE;

    RtlEnterCriticalSection( &csVirtual );  /* no need for signal masking inside signal handler */
1620
    if ((view = VIRTUAL_FindView( addr, 0 )))
1621 1622 1623 1624 1625 1626
    {
        void *page = ROUND_ADDR( addr, page_mask );
        BYTE vprot = view->prot[((const char *)page - (const char *)view->base) >> page_shift];
        if (vprot & VPROT_GUARD)
        {
            VIRTUAL_SetProt( view, page, page_size, vprot & ~VPROT_GUARD );
1627 1628 1629 1630 1631 1632
            NtCurrentTeb()->Tib.StackLimit = page;
            if ((char *)page >= (char *)NtCurrentTeb()->DeallocationStack + 2*page_size)
            {
                vprot = view->prot[((char *)page - page_size - (char *)view->base) >> page_shift];
                VIRTUAL_SetProt( view, (char *)page - page_size, page_size, vprot | VPROT_GUARD );
            }
1633 1634 1635 1636 1637 1638 1639 1640
            ret = TRUE;
        }
    }
    RtlLeaveCriticalSection( &csVirtual );
    return ret;
}


1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
/***********************************************************************
 *           virtual_check_buffer_for_read
 *
 * Check if a memory buffer can be read, triggering page faults if needed for DIB section access.
 */
BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size )
{
    if (!size) return TRUE;
    if (!ptr) return FALSE;

    __TRY
    {
        volatile const char *p = ptr;
        char dummy;
        SIZE_T count = size;

        while (count > page_size)
        {
            dummy = *p;
            p += page_size;
            count -= page_size;
        }
        dummy = p[0];
        dummy = p[count - 1];
    }
    __EXCEPT_PAGE_FAULT
    {
        return FALSE;
    }
    __ENDTRY
    return TRUE;
}


1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
/***********************************************************************
 *           virtual_check_buffer_for_write
 *
 * Check if a memory buffer can be written to, triggering page faults if needed for write watches.
 */
BOOL virtual_check_buffer_for_write( void *ptr, SIZE_T size )
{
    if (!size) return TRUE;
    if (!ptr) return FALSE;

    __TRY
    {
        volatile char *p = ptr;
        SIZE_T count = size;

        while (count > page_size)
        {
            *p |= 0;
            p += page_size;
            count -= page_size;
        }
        p[0] |= 0;
        p[count - 1] |= 0;
    }
    __EXCEPT_PAGE_FAULT
    {
        return FALSE;
    }
    __ENDTRY
    return TRUE;
}


1708 1709 1710 1711 1712 1713 1714 1715
/***********************************************************************
 *           VIRTUAL_SetForceExec
 *
 * Whether to force exec prot on all views.
 */
void VIRTUAL_SetForceExec( BOOL enable )
{
    struct file_view *view;
1716
    sigset_t sigset;
1717

1718
    server_enter_uninterrupted_section( &csVirtual, &sigset );
1719 1720 1721 1722 1723 1724 1725 1726
    if (!force_exec_prot != !enable)  /* change all existing views */
    {
        force_exec_prot = enable;

        LIST_FOR_EACH_ENTRY( view, &views_list, struct file_view, entry )
        {
            UINT i, count;
            char *addr = view->base;
1727 1728
            BYTE commit = view->mapping ? VPROT_COMMITTED : 0;  /* file mappings are always accessible */
            int unix_prot = VIRTUAL_GetUnixProt( view->prot[0] | commit );
1729

1730
            if (view->protect & VPROT_NOEXEC) continue;
1731 1732
            for (count = i = 1; i < view->size >> page_shift; i++, count++)
            {
1733 1734
                int prot = VIRTUAL_GetUnixProt( view->prot[i] | commit );
                if (prot == unix_prot) continue;
1735 1736 1737 1738 1739 1740 1741 1742 1743
                if ((unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
                {
                    TRACE( "%s exec prot for %p-%p\n",
                           force_exec_prot ? "enabling" : "disabling",
                           addr, addr + (count << page_shift) - 1 );
                    mprotect( addr, count << page_shift,
                              unix_prot | (force_exec_prot ? PROT_EXEC : 0) );
                }
                addr += (count << page_shift);
1744
                unix_prot = prot;
1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
                count = 0;
            }
            if (count)
            {
                if ((unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
                {
                    TRACE( "%s exec prot for %p-%p\n",
                           force_exec_prot ? "enabling" : "disabling",
                           addr, addr + (count << page_shift) - 1 );
                    mprotect( addr, count << page_shift,
                              unix_prot | (force_exec_prot ? PROT_EXEC : 0) );
                }
            }
        }
    }
1760
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1761 1762
}

1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784
struct free_range
{
    char *base;
    char *limit;
};

/* free reserved areas above the limit; callback for wine_mmap_enum_reserved_areas */
static int free_reserved_memory( void *base, size_t size, void *arg )
{
    struct free_range *range = arg;

    if ((char *)base >= range->limit) return 0;
    if ((char *)base + size <= range->base) return 0;
    if ((char *)base < range->base)
    {
        size -= range->base - (char *)base;
        base = range->base;
    }
    if ((char *)base + size > range->limit) size = range->limit - (char *)base;
    remove_reserved_area( base, size );
    return 1;  /* stop enumeration since the list has changed */
}
1785

1786
/***********************************************************************
1787
 *           virtual_release_address_space
1788
 *
1789
 * Release some address space once we have loaded and initialized the app.
1790
 */
1791
void virtual_release_address_space( BOOL free_high_mem )
1792
{
1793 1794 1795
    struct free_range range;
    sigset_t sigset;

1796 1797
    if (user_space_limit == address_space_limit) return;  /* no need to free anything */

1798 1799
    server_enter_uninterrupted_section( &csVirtual, &sigset );

1800
    /* no large address space on win9x */
1801 1802
    if (free_high_mem && NtCurrentTeb()->Peb->OSPlatformId == VER_PLATFORM_WIN32_NT)
    {
1803
        range.base  = (char *)0x82000000;
1804 1805 1806 1807
        range.limit = address_space_limit;
        while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 1 )) /* nothing */;
        user_space_limit = working_set_limit = address_space_limit;
    }
1808 1809 1810 1811 1812 1813 1814 1815 1816
    else
    {
#ifndef __APPLE__  /* dyld doesn't support parts of the WINE_DOS segment being unmapped */
        range.base  = (char *)0x20000000;
        range.limit = (char *)0x7f000000;
        while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 0 )) /* nothing */;
#endif
    }

1817
    server_leave_uninterrupted_section( &csVirtual, &sigset );
1818 1819 1820
}


1821 1822 1823 1824
/***********************************************************************
 *             NtAllocateVirtualMemory   (NTDLL.@)
 *             ZwAllocateVirtualMemory   (NTDLL.@)
 */
1825
NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_bits,
1826
                                         SIZE_T *size_ptr, ULONG type, ULONG protect )
1827 1828
{
    void *base;
1829
    unsigned int vprot;
1830
    SIZE_T size = *size_ptr;
1831
    SIZE_T mask = get_mask( zero_bits );
1832 1833
    NTSTATUS status = STATUS_SUCCESS;
    struct file_view *view;
1834
    sigset_t sigset;
1835

1836
    TRACE("%p %p %08lx %x %08x\n", process, *ret, size, type, protect );
1837 1838 1839

    if (!size) return STATUS_INVALID_PARAMETER;

1840
    if (process != NtCurrentProcess())
1841
    {
1842 1843 1844
        apc_call_t call;
        apc_result_t result;

1845 1846
        memset( &call, 0, sizeof(call) );

1847
        call.virtual_alloc.type      = APC_VIRTUAL_ALLOC;
1848
        call.virtual_alloc.addr      = wine_server_client_ptr( *ret );
1849 1850 1851 1852 1853 1854 1855 1856 1857
        call.virtual_alloc.size      = *size_ptr;
        call.virtual_alloc.zero_bits = zero_bits;
        call.virtual_alloc.op_type   = type;
        call.virtual_alloc.prot      = protect;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_alloc.status == STATUS_SUCCESS)
        {
1858
            *ret      = wine_server_get_ptr( result.virtual_alloc.addr );
1859 1860 1861
            *size_ptr = result.virtual_alloc.size;
        }
        return result.virtual_alloc.status;
1862 1863 1864 1865
    }

    /* Round parameters to a page boundary */

1866
    if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE;
1867

1868 1869
    if ((status = get_vprot_flags( protect, &vprot ))) return status;
    vprot |= VPROT_VALLOC;
1870 1871
    if (type & MEM_COMMIT) vprot |= VPROT_COMMITTED;

1872
    if (*ret)
1873 1874
    {
        if (type & MEM_RESERVE) /* Round down to 64k boundary */
1875
            base = ROUND_ADDR( *ret, mask );
1876
        else
1877 1878
            base = ROUND_ADDR( *ret, page_mask );
        size = (((UINT_PTR)*ret + size + page_mask) & ~page_mask) - (UINT_PTR)base;
1879

1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
        /* address 1 is magic to mean DOS area */
        if (!base && *ret == (void *)1 && size == 0x110000)
        {
            server_enter_uninterrupted_section( &csVirtual, &sigset );
            status = allocate_dos_memory( &view, vprot );
            if (status == STATUS_SUCCESS)
            {
                *ret = view->base;
                *size_ptr = view->size;
            }
            server_leave_uninterrupted_section( &csVirtual, &sigset );
            return status;
        }

1894
        /* disallow low 64k, wrap-around and kernel space */
1895
        if (((char *)base < (char *)0x10000) ||
1896
            ((char *)base + size < (char *)base) ||
1897
            is_beyond_limit( base, size, address_space_limit ))
1898 1899 1900 1901 1902 1903 1904 1905 1906 1907
            return STATUS_INVALID_PARAMETER;
    }
    else
    {
        base = NULL;
        size = (size + page_mask) & ~page_mask;
    }

    /* Compute the alloc type flags */

1908
    if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)) ||
1909
        (type & ~(MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_RESET)))
1910
    {
1911 1912 1913
        WARN("called with wrong alloc type flags (%08x) !\n", type);
        return STATUS_INVALID_PARAMETER;
    }
1914

1915 1916
    /* Reserve the memory */

1917
    if (use_locks) server_enter_uninterrupted_section( &csVirtual, &sigset );
1918

1919
    if ((type & MEM_RESERVE) || !base)
1920
    {
1921
        if (type & MEM_WRITE_WATCH) vprot |= VPROT_WRITEWATCH;
1922
        status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot );
1923
        if (status == STATUS_SUCCESS) base = view->base;
1924
    }
1925 1926 1927 1928 1929
    else if (type & MEM_RESET)
    {
        if (!(view = VIRTUAL_FindView( base, size ))) status = STATUS_NOT_MAPPED_VIEW;
        else madvise( base, size, MADV_DONTNEED );
    }
1930
    else  /* commit the pages */
1931
    {
1932
        if (!(view = VIRTUAL_FindView( base, size ))) status = STATUS_NOT_MAPPED_VIEW;
1933
        else if (!VIRTUAL_SetProt( view, base, size, vprot )) status = STATUS_ACCESS_DENIED;
1934 1935 1936 1937
        else if (view->mapping && !(view->protect & VPROT_COMMITTED))
        {
            SERVER_START_REQ( add_mapping_committed_range )
            {
1938
                req->handle = wine_server_obj_handle( view->mapping );
1939 1940 1941 1942 1943 1944
                req->offset = (char *)base - (char *)view->base;
                req->size   = size;
                wine_server_call( req );
            }
            SERVER_END_REQ;
        }
1945 1946
    }

1947
    if (use_locks) server_leave_uninterrupted_section( &csVirtual, &sigset );
1948 1949 1950 1951 1952 1953 1954

    if (status == STATUS_SUCCESS)
    {
        *ret = base;
        *size_ptr = size;
    }
    return status;
1955 1956 1957 1958 1959 1960 1961
}


/***********************************************************************
 *             NtFreeVirtualMemory   (NTDLL.@)
 *             ZwFreeVirtualMemory   (NTDLL.@)
 */
1962
NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *size_ptr, ULONG type )
1963
{
1964
    struct file_view *view;
1965
    char *base;
1966
    sigset_t sigset;
1967
    NTSTATUS status = STATUS_SUCCESS;
1968
    LPVOID addr = *addr_ptr;
1969
    SIZE_T size = *size_ptr;
1970

1971
    TRACE("%p %p %08lx %x\n", process, addr, size, type );
1972

1973
    if (process != NtCurrentProcess())
1974
    {
1975 1976 1977
        apc_call_t call;
        apc_result_t result;

1978 1979
        memset( &call, 0, sizeof(call) );

1980
        call.virtual_free.type      = APC_VIRTUAL_FREE;
1981
        call.virtual_free.addr      = wine_server_client_ptr( addr );
1982 1983 1984 1985 1986 1987 1988
        call.virtual_free.size      = size;
        call.virtual_free.op_type   = type;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_free.status == STATUS_SUCCESS)
        {
1989
            *addr_ptr = wine_server_get_ptr( result.virtual_free.addr );
1990 1991 1992
            *size_ptr = result.virtual_free.size;
        }
        return result.virtual_free.status;
1993 1994 1995 1996 1997 1998 1999
    }

    /* Fix the parameters */

    size = ROUND_SIZE( addr, size );
    base = ROUND_ADDR( addr, page_mask );

2000
    /* avoid freeing the DOS area when a broken app passes a NULL pointer */
2001
    if (!base) return STATUS_INVALID_PARAMETER;
2002

2003
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2004

2005
    if (!(view = VIRTUAL_FindView( base, size )) || !(view->protect & VPROT_VALLOC))
2006 2007 2008 2009
    {
        status = STATUS_INVALID_PARAMETER;
    }
    else if (type == MEM_RELEASE)
2010
    {
2011 2012
        /* Free the pages */

2013 2014 2015
        if (size || (base != view->base)) status = STATUS_INVALID_PARAMETER;
        else
        {
2016
            delete_view( view );
2017 2018 2019
            *addr_ptr = base;
            *size_ptr = size;
        }
2020
    }
2021
    else if (type == MEM_DECOMMIT)
2022
    {
2023 2024
        status = decommit_pages( view, base - (char *)view->base, size );
        if (status == STATUS_SUCCESS)
2025 2026 2027 2028
        {
            *addr_ptr = base;
            *size_ptr = size;
        }
2029
    }
2030 2031
    else
    {
2032
        WARN("called with wrong free type flags (%08x) !\n", type);
2033
        status = STATUS_INVALID_PARAMETER;
2034
    }
2035

2036
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2037
    return status;
2038 2039 2040 2041 2042 2043 2044
}


/***********************************************************************
 *             NtProtectVirtualMemory   (NTDLL.@)
 *             ZwProtectVirtualMemory   (NTDLL.@)
 */
2045
NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *size_ptr,
2046 2047
                                        ULONG new_prot, ULONG *old_prot )
{
2048
    struct file_view *view;
2049
    sigset_t sigset;
2050
    NTSTATUS status = STATUS_SUCCESS;
2051
    char *base;
2052
    BYTE vprot;
2053
    unsigned int new_vprot;
2054
    SIZE_T size = *size_ptr;
2055 2056
    LPVOID addr = *addr_ptr;

2057
    TRACE("%p %p %08lx %08x\n", process, addr, size, new_prot );
2058

2059
    if (process != NtCurrentProcess())
2060
    {
2061 2062 2063
        apc_call_t call;
        apc_result_t result;

2064 2065
        memset( &call, 0, sizeof(call) );

2066
        call.virtual_protect.type = APC_VIRTUAL_PROTECT;
2067
        call.virtual_protect.addr = wine_server_client_ptr( addr );
2068 2069 2070 2071 2072 2073 2074
        call.virtual_protect.size = size;
        call.virtual_protect.prot = new_prot;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_protect.status == STATUS_SUCCESS)
        {
2075
            *addr_ptr = wine_server_get_ptr( result.virtual_protect.addr );
2076 2077 2078 2079
            *size_ptr = result.virtual_protect.size;
            if (old_prot) *old_prot = result.virtual_protect.prot;
        }
        return result.virtual_protect.status;
2080 2081 2082 2083 2084 2085
    }

    /* Fix the parameters */

    size = ROUND_SIZE( addr, size );
    base = ROUND_ADDR( addr, page_mask );
2086 2087
    if ((status = get_vprot_flags( new_prot, &new_vprot ))) return status;
    new_vprot |= VPROT_COMMITTED;
2088

2089
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2090

2091
    if (!(view = VIRTUAL_FindView( base, size )))
2092
    {
2093
        status = STATUS_INVALID_PARAMETER;
2094
    }
2095 2096 2097
    else
    {
        /* Make sure all the pages are committed */
2098
        if (get_committed_size( view, base, &vprot ) >= size && (vprot & VPROT_COMMITTED))
2099
        {
2100
            if (old_prot) *old_prot = VIRTUAL_GetWin32Prot( vprot );
2101
            if (!VIRTUAL_SetProt( view, base, size, new_vprot )) status = STATUS_ACCESS_DENIED;
2102
        }
2103
        else status = STATUS_NOT_COMMITTED;
2104
    }
2105
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2106

2107 2108 2109 2110 2111 2112
    if (status == STATUS_SUCCESS)
    {
        *addr_ptr = base;
        *size_ptr = size;
    }
    return status;
2113 2114
}

2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129

/* retrieve state for a free memory area; callback for wine_mmap_enum_reserved_areas */
static int get_free_mem_state_callback( void *start, size_t size, void *arg )
{
    MEMORY_BASIC_INFORMATION *info = arg;
    void *end = (char *)start + size;

    if ((char *)info->BaseAddress + info->RegionSize < (char *)start) return 0;

    if (info->BaseAddress >= end)
    {
        if (info->AllocationBase < end) info->AllocationBase = end;
        return 0;
    }

2130
    if (info->BaseAddress >= start || start <= address_space_start)
2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151
    {
        /* it's a real free area */
        info->State             = MEM_FREE;
        info->Protect           = PAGE_NOACCESS;
        info->AllocationBase    = 0;
        info->AllocationProtect = 0;
        info->Type              = 0;
        if ((char *)info->BaseAddress + info->RegionSize > (char *)end)
            info->RegionSize = (char *)end - (char *)info->BaseAddress;
    }
    else /* outside of the reserved area, pretend it's allocated */
    {
        info->RegionSize        = (char *)start - (char *)info->BaseAddress;
        info->State             = MEM_RESERVE;
        info->Protect           = PAGE_NOACCESS;
        info->AllocationProtect = PAGE_NOACCESS;
        info->Type              = MEM_PRIVATE;
    }
    return 1;
}

2152 2153 2154 2155
#define UNIMPLEMENTED_INFO_CLASS(c) \
    case c: \
        FIXME("(process=%p,addr=%p) Unimplemented information class: " #c "\n", process, addr); \
        return STATUS_INVALID_INFO_CLASS
2156 2157 2158 2159 2160 2161 2162

/***********************************************************************
 *             NtQueryVirtualMemory   (NTDLL.@)
 *             ZwQueryVirtualMemory   (NTDLL.@)
 */
NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
                                      MEMORY_INFORMATION_CLASS info_class, PVOID buffer,
2163
                                      SIZE_T len, SIZE_T *res_len )
2164
{
2165
    struct file_view *view;
2166
    char *base, *alloc_base = 0;
2167
    struct list *ptr;
2168
    SIZE_T size = 0;
2169
    MEMORY_BASIC_INFORMATION *info = buffer;
2170
    sigset_t sigset;
2171

2172 2173
    if (info_class != MemoryBasicInformation)
    {
2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184
        switch(info_class)
        {
            UNIMPLEMENTED_INFO_CLASS(MemoryWorkingSetList);
            UNIMPLEMENTED_INFO_CLASS(MemorySectionName);
            UNIMPLEMENTED_INFO_CLASS(MemoryBasicVlmInformation);

            default:
                FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n", 
                      process, addr, info_class, buffer, len, res_len);
                return STATUS_INVALID_INFO_CLASS;
        }
2185
    }
2186

2187
    if (process != NtCurrentProcess())
2188
    {
2189 2190 2191 2192
        NTSTATUS status;
        apc_call_t call;
        apc_result_t result;

2193 2194
        memset( &call, 0, sizeof(call) );

2195
        call.virtual_query.type = APC_VIRTUAL_QUERY;
2196
        call.virtual_query.addr = wine_server_client_ptr( addr );
2197 2198 2199 2200 2201
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_query.status == STATUS_SUCCESS)
        {
2202 2203
            info->BaseAddress       = wine_server_get_ptr( result.virtual_query.base );
            info->AllocationBase    = wine_server_get_ptr( result.virtual_query.alloc_base );
2204 2205 2206
            info->RegionSize        = result.virtual_query.size;
            info->Protect           = result.virtual_query.prot;
            info->AllocationProtect = result.virtual_query.alloc_prot;
2207 2208
            info->State             = (DWORD)result.virtual_query.state << 12;
            info->Type              = (DWORD)result.virtual_query.alloc_type << 16;
2209 2210
            if (info->RegionSize != result.virtual_query.size)  /* truncated */
                return STATUS_INVALID_PARAMETER;  /* FIXME */
2211 2212 2213
            if (res_len) *res_len = sizeof(*info);
        }
        return result.virtual_query.status;
2214 2215 2216 2217
    }

    base = ROUND_ADDR( addr, page_mask );

2218 2219
    if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE;

2220 2221
    /* Find the view containing the address */

2222
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2223
    ptr = list_head( &views_list );
2224 2225
    for (;;)
    {
2226
        if (!ptr)
2227
        {
2228
            size = (char *)working_set_limit - alloc_base;
2229
            view = NULL;
2230 2231
            break;
        }
2232
        view = LIST_ENTRY( ptr, struct file_view, entry );
2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245
        if ((char *)view->base > base)
        {
            size = (char *)view->base - alloc_base;
            view = NULL;
            break;
        }
        if ((char *)view->base + view->size > base)
        {
            alloc_base = view->base;
            size = view->size;
            break;
        }
        alloc_base = (char *)view->base + view->size;
2246
        ptr = list_next( &views_list, ptr );
2247 2248 2249 2250
    }

    /* Fill the info structure */

2251 2252 2253 2254
    info->AllocationBase = alloc_base;
    info->BaseAddress    = base;
    info->RegionSize     = size - (base - alloc_base);

2255 2256
    if (!view)
    {
2257 2258 2259
        if (!wine_mmap_enum_reserved_areas( get_free_mem_state_callback, info, 0 ))
        {
            /* not in a reserved area at all, pretend it's allocated */
2260
#ifdef __i386__
2261 2262 2263 2264 2265 2266 2267 2268
            if (base >= (char *)address_space_start)
            {
                info->State             = MEM_RESERVE;
                info->Protect           = PAGE_NOACCESS;
                info->AllocationProtect = PAGE_NOACCESS;
                info->Type              = MEM_PRIVATE;
            }
            else
2269
#endif
2270 2271 2272 2273 2274 2275 2276
            {
                info->State             = MEM_FREE;
                info->Protect           = PAGE_NOACCESS;
                info->AllocationBase    = 0;
                info->AllocationProtect = 0;
                info->Type              = 0;
            }
2277
        }
2278 2279 2280
    }
    else
    {
2281 2282 2283
        BYTE vprot;
        SIZE_T range_size = get_committed_size( view, base, &vprot );

2284
        info->State = (vprot & VPROT_COMMITTED) ? MEM_COMMIT : MEM_RESERVE;
2285
        info->Protect = (vprot & VPROT_COMMITTED) ? VIRTUAL_GetWin32Prot( vprot ) : 0;
2286
        info->AllocationBase = alloc_base;
2287
        info->AllocationProtect = VIRTUAL_GetWin32Prot( view->protect );
2288
        if (view->protect & VPROT_IMAGE) info->Type = MEM_IMAGE;
2289
        else if (view->protect & VPROT_VALLOC) info->Type = MEM_PRIVATE;
2290
        else info->Type = MEM_MAPPED;
2291
        for (size = base - alloc_base; size < base + range_size - alloc_base; size += page_size)
2292
            if ((view->prot[size >> page_shift] ^ vprot) & ~VPROT_WRITEWATCH) break;
2293
        info->RegionSize = size - (base - alloc_base);
2294
    }
2295
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2296

2297
    if (res_len) *res_len = sizeof(*info);
2298 2299 2300 2301 2302 2303 2304 2305
    return STATUS_SUCCESS;
}


/***********************************************************************
 *             NtLockVirtualMemory   (NTDLL.@)
 *             ZwLockVirtualMemory   (NTDLL.@)
 */
2306
NTSTATUS WINAPI NtLockVirtualMemory( HANDLE process, PVOID *addr, SIZE_T *size, ULONG unknown )
2307
{
2308 2309 2310
    NTSTATUS status = STATUS_SUCCESS;

    if (process != NtCurrentProcess())
2311
    {
2312 2313 2314
        apc_call_t call;
        apc_result_t result;

2315 2316
        memset( &call, 0, sizeof(call) );

2317
        call.virtual_lock.type = APC_VIRTUAL_LOCK;
2318
        call.virtual_lock.addr = wine_server_client_ptr( *addr );
2319 2320 2321 2322 2323 2324
        call.virtual_lock.size = *size;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_lock.status == STATUS_SUCCESS)
        {
2325
            *addr = wine_server_get_ptr( result.virtual_lock.addr );
2326 2327 2328
            *size = result.virtual_lock.size;
        }
        return result.virtual_lock.status;
2329
    }
2330 2331 2332 2333 2334 2335

    *size = ROUND_SIZE( *addr, *size );
    *addr = ROUND_ADDR( *addr, page_mask );

    if (mlock( *addr, *size )) status = STATUS_ACCESS_DENIED;
    return status;
2336 2337 2338 2339 2340 2341 2342
}


/***********************************************************************
 *             NtUnlockVirtualMemory   (NTDLL.@)
 *             ZwUnlockVirtualMemory   (NTDLL.@)
 */
2343
NTSTATUS WINAPI NtUnlockVirtualMemory( HANDLE process, PVOID *addr, SIZE_T *size, ULONG unknown )
2344
{
2345 2346 2347
    NTSTATUS status = STATUS_SUCCESS;

    if (process != NtCurrentProcess())
2348
    {
2349 2350 2351
        apc_call_t call;
        apc_result_t result;

2352 2353
        memset( &call, 0, sizeof(call) );

2354
        call.virtual_unlock.type = APC_VIRTUAL_UNLOCK;
2355
        call.virtual_unlock.addr = wine_server_client_ptr( *addr );
2356 2357 2358 2359 2360 2361
        call.virtual_unlock.size = *size;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_unlock.status == STATUS_SUCCESS)
        {
2362
            *addr = wine_server_get_ptr( result.virtual_unlock.addr );
2363 2364 2365
            *size = result.virtual_unlock.size;
        }
        return result.virtual_unlock.status;
2366
    }
2367 2368 2369 2370 2371 2372

    *size = ROUND_SIZE( *addr, *size );
    *addr = ROUND_ADDR( *addr, page_mask );

    if (munlock( *addr, *size )) status = STATUS_ACCESS_DENIED;
    return status;
2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384
}


/***********************************************************************
 *             NtCreateSection   (NTDLL.@)
 *             ZwCreateSection   (NTDLL.@)
 */
NTSTATUS WINAPI NtCreateSection( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
                                 const LARGE_INTEGER *size, ULONG protect,
                                 ULONG sec_flags, HANDLE file )
{
    NTSTATUS ret;
2385
    unsigned int vprot;
2386
    DWORD len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0;
2387 2388
    struct security_descriptor *sd = NULL;
    struct object_attributes objattr;
2389 2390 2391 2392 2393

    /* Check parameters */

    if (len > MAX_PATH*sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

2394 2395
    if ((ret = get_vprot_flags( protect, &vprot ))) return ret;

2396
    objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 );
2397
    objattr.sd_len = 0;
2398
    objattr.name_len = len;
2399 2400 2401 2402 2403 2404
    if (attr)
    {
        ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len );
        if (ret != STATUS_SUCCESS) return ret;
    }

2405
    if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED;
2406 2407 2408 2409 2410 2411 2412
    if (sec_flags & SEC_NOCACHE) vprot |= VPROT_NOCACHE;
    if (sec_flags & SEC_IMAGE) vprot |= VPROT_IMAGE;

    /* Create the server object */

    SERVER_START_REQ( create_mapping )
    {
2413 2414
        req->access      = access;
        req->attributes  = (attr) ? attr->Attributes : 0;
2415
        req->file_handle = wine_server_obj_handle( file );
2416
        req->size        = size ? size->QuadPart : 0;
2417
        req->protect     = vprot;
2418 2419
        wine_server_add_data( req, &objattr, sizeof(objattr) );
        if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len );
2420 2421
        if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
        ret = wine_server_call( req );
2422
        *handle = wine_server_ptr_handle( reply->handle );
2423 2424
    }
    SERVER_END_REQ;
2425 2426 2427

    NTDLL_free_struct_sd( sd );

2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445
    return ret;
}


/***********************************************************************
 *             NtOpenSection   (NTDLL.@)
 *             ZwOpenSection   (NTDLL.@)
 */
NTSTATUS WINAPI NtOpenSection( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
    NTSTATUS ret;
    DWORD len = attr->ObjectName->Length;

    if (len > MAX_PATH*sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;

    SERVER_START_REQ( open_mapping )
    {
        req->access  = access;
2446
        req->attributes = attr->Attributes;
2447
        req->rootdir = wine_server_obj_handle( attr->RootDirectory );
2448
        wine_server_add_data( req, attr->ObjectName->Buffer, len );
2449
        if (!(ret = wine_server_call( req ))) *handle = wine_server_ptr_handle( reply->handle );
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460
    }
    SERVER_END_REQ;
    return ret;
}


/***********************************************************************
 *             NtMapViewOfSection   (NTDLL.@)
 *             ZwMapViewOfSection   (NTDLL.@)
 */
NTSTATUS WINAPI NtMapViewOfSection( HANDLE handle, HANDLE process, PVOID *addr_ptr, ULONG zero_bits,
2461
                                    SIZE_T commit_size, const LARGE_INTEGER *offset_ptr, SIZE_T *size_ptr,
2462 2463 2464
                                    SECTION_INHERIT inherit, ULONG alloc_type, ULONG protect )
{
    NTSTATUS res;
2465
    mem_size_t full_size;
2466
    ACCESS_MASK access;
2467
    SIZE_T size, mask = get_mask( zero_bits );
2468
    int unix_handle = -1, needs_close;
2469
    unsigned int map_vprot, vprot;
2470 2471
    void *base;
    struct file_view *view;
2472
    DWORD header_size;
2473
    HANDLE dup_mapping, shared_file;
2474
    LARGE_INTEGER offset;
2475
    sigset_t sigset;
2476 2477

    offset.QuadPart = offset_ptr ? offset_ptr->QuadPart : 0;
2478

2479
    TRACE("handle=%p process=%p addr=%p off=%x%08x size=%lx access=%x\n",
2480
          handle, process, *addr_ptr, offset.u.HighPart, offset.u.LowPart, *size_ptr, protect );
2481

2482 2483
    /* Check parameters */

2484
    if ((offset.u.LowPart & mask) || (*addr_ptr && ((UINT_PTR)*addr_ptr & mask)))
2485 2486
        return STATUS_INVALID_PARAMETER;

2487 2488 2489
    switch(protect)
    {
    case PAGE_NOACCESS:
2490
        access = 0;
2491 2492 2493
        break;
    case PAGE_READWRITE:
    case PAGE_EXECUTE_READWRITE:
2494
        access = SECTION_MAP_WRITE;
2495 2496 2497 2498 2499 2500
        break;
    case PAGE_READONLY:
    case PAGE_WRITECOPY:
    case PAGE_EXECUTE:
    case PAGE_EXECUTE_READ:
    case PAGE_EXECUTE_WRITECOPY:
2501
        access = SECTION_MAP_READ;
2502 2503 2504 2505 2506
        break;
    default:
        return STATUS_INVALID_PARAMETER;
    }

2507 2508 2509 2510 2511
    if (process != NtCurrentProcess())
    {
        apc_call_t call;
        apc_result_t result;

2512 2513
        memset( &call, 0, sizeof(call) );

2514
        call.map_view.type        = APC_MAP_VIEW;
2515
        call.map_view.handle      = wine_server_obj_handle( handle );
2516
        call.map_view.addr        = wine_server_client_ptr( *addr_ptr );
2517
        call.map_view.size        = *size_ptr;
2518
        call.map_view.offset      = offset.QuadPart;
2519 2520 2521 2522 2523 2524
        call.map_view.zero_bits   = zero_bits;
        call.map_view.alloc_type  = alloc_type;
        call.map_view.prot        = protect;
        res = NTDLL_queue_process_apc( process, &call, &result );
        if (res != STATUS_SUCCESS) return res;

2525
        if ((NTSTATUS)result.map_view.status >= 0)
2526
        {
2527
            *addr_ptr = wine_server_get_ptr( result.map_view.addr );
2528 2529 2530 2531 2532
            *size_ptr = result.map_view.size;
        }
        return result.map_view.status;
    }

2533 2534
    SERVER_START_REQ( get_mapping_info )
    {
2535
        req->handle = wine_server_obj_handle( handle );
2536
        req->access = access;
2537
        res = wine_server_call( req );
2538
        map_vprot   = reply->protect;
2539
        base        = wine_server_get_ptr( reply->base );
2540
        full_size   = reply->size;
2541
        header_size = reply->header_size;
2542 2543
        dup_mapping = wine_server_ptr_handle( reply->mapping );
        shared_file = wine_server_ptr_handle( reply->shared_file );
2544
        if ((ULONG_PTR)base != reply->base) base = NULL;
2545 2546
    }
    SERVER_END_REQ;
2547
    if (res) return res;
2548

2549
    if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL ))) goto done;
2550

2551
    if (map_vprot & VPROT_IMAGE)
2552
    {
2553 2554 2555 2556 2557 2558 2559
        size = full_size;
        if (size != full_size)  /* truncated */
        {
            WARN( "Modules larger than 4Gb (%s) not supported\n", wine_dbgstr_longlong(full_size) );
            res = STATUS_INVALID_PARAMETER;
            goto done;
        }
2560 2561
        if (shared_file)
        {
2562
            int shared_fd, shared_needs_close;
2563

2564
            if ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA,
2565
                                           &shared_fd, &shared_needs_close, NULL, NULL ))) goto done;
2566
            res = map_image( handle, unix_handle, base, size, mask, header_size,
2567
                             shared_fd, dup_mapping, addr_ptr );
2568
            if (shared_needs_close) close( shared_fd );
2569 2570 2571 2572
            NtClose( shared_file );
        }
        else
        {
2573
            res = map_image( handle, unix_handle, base, size, mask, header_size,
2574
                             -1, dup_mapping, addr_ptr );
2575
        }
2576
        if (needs_close) close( unix_handle );
2577
        if (res >= 0) *size_ptr = size;
2578 2579 2580
        return res;
    }

2581 2582 2583
    res = STATUS_INVALID_PARAMETER;
    if (offset.QuadPart >= full_size) goto done;
    if (*size_ptr)
2584
    {
2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
        if (*size_ptr > full_size - offset.QuadPart) goto done;
        size = ROUND_SIZE( offset.u.LowPart, *size_ptr );
        if (size < *size_ptr) goto done;  /* wrap-around */
    }
    else
    {
        size = full_size - offset.QuadPart;
        if (size != full_size - offset.QuadPart)  /* truncated */
        {
            WARN( "Files larger than 4Gb (%s) not supported on this platform\n",
                  wine_dbgstr_longlong(full_size) );
            goto done;
        }
2598 2599 2600 2601
    }

    /* Reserve a properly aligned area */

2602
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2603

2604 2605
    get_vprot_flags( protect, &vprot );
    vprot |= (map_vprot & VPROT_COMMITTED);
2606
    res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot );
2607 2608
    if (res)
    {
2609
        server_leave_uninterrupted_section( &csVirtual, &sigset );
2610 2611
        goto done;
    }
2612 2613 2614

    /* Map the file */

2615
    TRACE("handle=%p size=%lx offset=%x%08x\n",
2616
          handle, size, offset.u.HighPart, offset.u.LowPart );
2617

2618
    res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, !dup_mapping );
2619
    if (res == STATUS_SUCCESS)
2620
    {
2621 2622
        *addr_ptr = view->base;
        *size_ptr = size;
2623 2624
        view->mapping = dup_mapping;
        dup_mapping = 0;  /* don't close it */
2625 2626
    }
    else
2627
    {
2628
        ERR( "map_file_into_view %p %lx %x%08x failed\n",
2629
             view->base, size, offset.u.HighPart, offset.u.LowPart );
2630
        delete_view( view );
2631 2632
    }

2633
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2634 2635

done:
2636
    if (dup_mapping) NtClose( dup_mapping );
2637
    if (needs_close) close( unix_handle );
2638 2639 2640 2641 2642 2643 2644 2645 2646 2647
    return res;
}


/***********************************************************************
 *             NtUnmapViewOfSection   (NTDLL.@)
 *             ZwUnmapViewOfSection   (NTDLL.@)
 */
NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr )
{
2648
    struct file_view *view;
2649
    NTSTATUS status = STATUS_NOT_MAPPED_VIEW;
2650
    sigset_t sigset;
2651 2652
    void *base = ROUND_ADDR( addr, page_mask );

2653
    if (process != NtCurrentProcess())
2654
    {
2655 2656 2657
        apc_call_t call;
        apc_result_t result;

2658 2659
        memset( &call, 0, sizeof(call) );

2660
        call.unmap_view.type = APC_UNMAP_VIEW;
2661
        call.unmap_view.addr = wine_server_client_ptr( addr );
2662 2663 2664
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status == STATUS_SUCCESS) status = result.unmap_view.status;
        return status;
2665
    }
2666

2667
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2668
    if ((view = VIRTUAL_FindView( base, 0 )) && (base == view->base) && !(view->protect & VPROT_VALLOC))
2669
    {
2670
        delete_view( view );
2671 2672
        status = STATUS_SUCCESS;
    }
2673
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2674
    return status;
2675 2676 2677 2678 2679 2680 2681 2682
}


/***********************************************************************
 *             NtFlushVirtualMemory   (NTDLL.@)
 *             ZwFlushVirtualMemory   (NTDLL.@)
 */
NTSTATUS WINAPI NtFlushVirtualMemory( HANDLE process, LPCVOID *addr_ptr,
2683
                                      SIZE_T *size_ptr, ULONG unknown )
2684
{
2685
    struct file_view *view;
2686
    NTSTATUS status = STATUS_SUCCESS;
2687
    sigset_t sigset;
2688 2689
    void *addr = ROUND_ADDR( *addr_ptr, page_mask );

2690
    if (process != NtCurrentProcess())
2691
    {
2692 2693 2694
        apc_call_t call;
        apc_result_t result;

2695 2696
        memset( &call, 0, sizeof(call) );

2697
        call.virtual_flush.type = APC_VIRTUAL_FLUSH;
2698
        call.virtual_flush.addr = wine_server_client_ptr( addr );
2699 2700 2701 2702 2703 2704
        call.virtual_flush.size = *size_ptr;
        status = NTDLL_queue_process_apc( process, &call, &result );
        if (status != STATUS_SUCCESS) return status;

        if (result.virtual_flush.status == STATUS_SUCCESS)
        {
2705
            *addr_ptr = wine_server_get_ptr( result.virtual_flush.addr );
2706 2707 2708
            *size_ptr = result.virtual_flush.size;
        }
        return result.virtual_flush.status;
2709
    }
2710

2711
    server_enter_uninterrupted_section( &csVirtual, &sigset );
2712
    if (!(view = VIRTUAL_FindView( addr, *size_ptr ))) status = STATUS_INVALID_PARAMETER;
2713 2714 2715 2716 2717 2718
    else
    {
        if (!*size_ptr) *size_ptr = view->size;
        *addr_ptr = addr;
        if (msync( addr, *size_ptr, MS_SYNC )) status = STATUS_NOT_MAPPED_DATA;
    }
2719
    server_leave_uninterrupted_section( &csVirtual, &sigset );
2720
    return status;
2721
}
2722 2723


2724 2725 2726 2727 2728 2729 2730
/***********************************************************************
 *             NtGetWriteWatch   (NTDLL.@)
 *             ZwGetWriteWatch   (NTDLL.@)
 */
NTSTATUS WINAPI NtGetWriteWatch( HANDLE process, ULONG flags, PVOID base, SIZE_T size, PVOID *addresses,
                                 ULONG_PTR *count, ULONG *granularity )
{
2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744
    struct file_view *view;
    NTSTATUS status = STATUS_SUCCESS;
    sigset_t sigset;

    size = ROUND_SIZE( base, size );
    base = ROUND_ADDR( base, page_mask );

    if (!count || !granularity) return STATUS_ACCESS_VIOLATION;
    if (!*count || !size) return STATUS_INVALID_PARAMETER;
    if (flags & ~WRITE_WATCH_FLAG_RESET) return STATUS_INVALID_PARAMETER;

    if (!addresses) return STATUS_ACCESS_VIOLATION;

    TRACE( "%p %x %p-%p %p %lu\n", process, flags, base, (char *)base + size,
2745
           addresses, *count );
2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768

    server_enter_uninterrupted_section( &csVirtual, &sigset );

    if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
    {
        ULONG_PTR pos = 0;
        char *addr = base;
        char *end = addr + size;

        while (pos < *count && addr < end)
        {
            BYTE prot = view->prot[(addr - (char *)view->base) >> page_shift];
            if (!(prot & VPROT_WRITEWATCH)) addresses[pos++] = addr;
            addr += page_size;
        }
        if (flags & WRITE_WATCH_FLAG_RESET) reset_write_watches( view, base, addr - (char *)base );
        *count = pos;
        *granularity = page_size;
    }
    else status = STATUS_INVALID_PARAMETER;

    server_leave_uninterrupted_section( &csVirtual, &sigset );
    return status;
2769 2770 2771 2772 2773 2774 2775 2776 2777
}


/***********************************************************************
 *             NtResetWriteWatch   (NTDLL.@)
 *             ZwResetWriteWatch   (NTDLL.@)
 */
NTSTATUS WINAPI NtResetWriteWatch( HANDLE process, PVOID base, SIZE_T size )
{
2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797
    struct file_view *view;
    NTSTATUS status = STATUS_SUCCESS;
    sigset_t sigset;

    size = ROUND_SIZE( base, size );
    base = ROUND_ADDR( base, page_mask );

    TRACE( "%p %p-%p\n", process, base, (char *)base + size );

    if (!size) return STATUS_INVALID_PARAMETER;

    server_enter_uninterrupted_section( &csVirtual, &sigset );

    if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
        reset_write_watches( view, base, size );
    else
        status = STATUS_INVALID_PARAMETER;

    server_leave_uninterrupted_section( &csVirtual, &sigset );
    return status;
2798 2799 2800
}


2801 2802 2803 2804 2805 2806 2807 2808 2809
/***********************************************************************
 *             NtReadVirtualMemory   (NTDLL.@)
 *             ZwReadVirtualMemory   (NTDLL.@)
 */
NTSTATUS WINAPI NtReadVirtualMemory( HANDLE process, const void *addr, void *buffer,
                                     SIZE_T size, SIZE_T *bytes_read )
{
    NTSTATUS status;

2810
    if (virtual_check_buffer_for_write( buffer, size ))
2811
    {
2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824
        SERVER_START_REQ( read_process_memory )
        {
            req->handle = wine_server_obj_handle( process );
            req->addr   = wine_server_client_ptr( addr );
            wine_server_set_reply( req, buffer, size );
            if ((status = wine_server_call( req ))) size = 0;
        }
        SERVER_END_REQ;
    }
    else
    {
        status = STATUS_ACCESS_VIOLATION;
        size = 0;
2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839
    }
    if (bytes_read) *bytes_read = size;
    return status;
}


/***********************************************************************
 *             NtWriteVirtualMemory   (NTDLL.@)
 *             ZwWriteVirtualMemory   (NTDLL.@)
 */
NTSTATUS WINAPI NtWriteVirtualMemory( HANDLE process, void *addr, const void *buffer,
                                      SIZE_T size, SIZE_T *bytes_written )
{
    NTSTATUS status;

2840
    if (virtual_check_buffer_for_read( buffer, size ))
2841
    {
2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854
        SERVER_START_REQ( write_process_memory )
        {
            req->handle     = wine_server_obj_handle( process );
            req->addr       = wine_server_client_ptr( addr );
            wine_server_add_data( req, buffer, size );
            if ((status = wine_server_call( req ))) size = 0;
        }
        SERVER_END_REQ;
    }
    else
    {
        status = STATUS_PARTIAL_COPY;
        size = 0;
2855 2856 2857 2858
    }
    if (bytes_written) *bytes_written = size;
    return status;
}
2859 2860 2861 2862 2863 2864 2865 2866


/***********************************************************************
 *             NtAreMappedFilesTheSame   (NTDLL.@)
 *             ZwAreMappedFilesTheSame   (NTDLL.@)
 */
NTSTATUS WINAPI NtAreMappedFilesTheSame(PVOID addr1, PVOID addr2)
{
2867 2868 2869 2870 2871
    struct file_view *view1, *view2;
    struct stat st1, st2;
    NTSTATUS status;
    sigset_t sigset;

2872 2873
    TRACE("%p %p\n", addr1, addr2);

2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894
    server_enter_uninterrupted_section( &csVirtual, &sigset );

    view1 = VIRTUAL_FindView( addr1, 0 );
    view2 = VIRTUAL_FindView( addr2, 0 );

    if (!view1 || !view2)
        status = STATUS_INVALID_ADDRESS;
    else if ((view1->protect & VPROT_VALLOC) || (view2->protect & VPROT_VALLOC))
        status = STATUS_CONFLICTING_ADDRESSES;
    else if (!(view1->protect & VPROT_IMAGE) || !(view2->protect & VPROT_IMAGE))
        status = STATUS_NOT_SAME_DEVICE;
    else if (view1 == view2)
        status = STATUS_SUCCESS;
    else if (!stat_mapping_file( view1, &st1 ) && !stat_mapping_file( view2, &st2 ) &&
             st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
        status = STATUS_SUCCESS;
    else
        status = STATUS_NOT_SAME_DEVICE;

    server_leave_uninterrupted_section( &csVirtual, &sigset );
    return status;
2895
}