wine-wine/dlls/ntdll/unix/virtual.c

214 lines
6.4 KiB
C
Raw Normal View History

/*
* Unix interface for virtual memory functions
*
* Copyright (C) 2020 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
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#if defined(__APPLE__)
# include <mach/mach_init.h>
# include <mach/mach_vm.h>
#endif
#include <stdio.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winnt.h"
#include "winternl.h"
#include "unixlib.h"
#include "wine/library.h"
struct preload_info
{
void *addr;
size_t size;
};
static const unsigned int granularity_mask = 0xffff; /* reserved areas have 64k granularity */
extern IMAGE_NT_HEADERS __wine_spec_nt_header;
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif
#ifndef MAP_TRYFIXED
#define MAP_TRYFIXED 0
#endif
static void reserve_area( void *addr, void *end )
{
#ifdef __APPLE__
#ifdef __i386__
static const mach_vm_address_t max_address = VM_MAX_ADDRESS;
#else
static const mach_vm_address_t max_address = MACH_VM_MAX_ADDRESS;
#endif
mach_vm_address_t address = (mach_vm_address_t)addr;
mach_vm_address_t end_address = (mach_vm_address_t)end;
if (!end_address || max_address < end_address)
end_address = max_address;
while (address < end_address)
{
mach_vm_address_t hole_address = address;
kern_return_t ret;
mach_vm_size_t size;
vm_region_basic_info_data_64_t info;
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t dummy_object_name = MACH_PORT_NULL;
/* find the mapped region at or above the current address. */
ret = mach_vm_region(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO_64,
(vm_region_info_t)&info, &count, &dummy_object_name);
if (ret != KERN_SUCCESS)
{
address = max_address;
size = 0;
}
if (end_address < address)
address = end_address;
if (hole_address < address)
{
/* found a hole, attempt to reserve it. */
size_t hole_size = address - hole_address;
mach_vm_address_t alloc_address = hole_address;
ret = mach_vm_map( mach_task_self(), &alloc_address, hole_size, 0, VM_FLAGS_FIXED,
MEMORY_OBJECT_NULL, 0, 0, PROT_NONE, VM_PROT_ALL, VM_INHERIT_COPY );
if (!ret) wine_mmap_add_reserved_area( (void*)hole_address, hole_size );
else if (ret == KERN_NO_SPACE)
{
/* something filled (part of) the hole before we could.
go back and look again. */
address = hole_address;
continue;
}
}
address += size;
}
#else
void *ptr;
int flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_TRYFIXED;
size_t size = (char *)end - (char *)addr;
if (!size) return;
ptr = mmap( addr, size, PROT_NONE, flags, -1, 0 );
if (ptr == addr)
{
wine_mmap_add_reserved_area( addr, size );
return;
}
if (ptr != (void *)-1) munmap( ptr, size );
size = (size / 2) & ~granularity_mask;
if (size)
{
reserve_area( addr, (char *)addr + size );
reserve_area( (char *)addr + size, end );
}
#endif /* __APPLE__ */
}
static void mmap_init( const struct preload_info *preload_info )
{
#ifdef __i386__
#ifndef __APPLE__
char stack;
char * const stack_ptr = &stack;
#endif
char *user_space_limit = (char *)0x7ffe0000;
int i;
if (preload_info)
{
/* check for a reserved area starting at the user space limit */
/* to avoid wasting time trying to allocate it again */
for (i = 0; preload_info[i].size; i++)
{
if ((char *)preload_info[i].addr > user_space_limit) break;
if ((char *)preload_info[i].addr + preload_info[i].size > user_space_limit)
{
user_space_limit = (char *)preload_info[i].addr + preload_info[i].size;
break;
}
}
}
else reserve_area( (void *)0x00010000, (void *)0x40000000 );
#ifndef __APPLE__
if (stack_ptr >= user_space_limit)
{
char *end = 0;
char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
if (base > user_space_limit) reserve_area( user_space_limit, base );
base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
#if defined(linux) || defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
/* Heuristic: assume the stack is near the end of the address */
/* space, this avoids a lot of futile allocation attempts */
end = (char *)(((unsigned long)base + 0x0fffffff) & 0xf0000000);
#endif
reserve_area( base, end );
}
else
#endif
reserve_area( user_space_limit, 0 );
#elif defined(__x86_64__) || defined(__aarch64__)
if (preload_info) return;
/* if we don't have a preloader, try to reserve the space now */
reserve_area( (void *)0x000000010000, (void *)0x000068000000 );
reserve_area( (void *)0x00007ff00000, (void *)0x00007fff0000 );
reserve_area( (void *)0x7ffffe000000, (void *)0x7fffffff0000 );
#endif
}
void virtual_init(void)
{
const struct preload_info **preload_info = dlsym( RTLD_DEFAULT, "wine_main_preload_info" );
int i;
if (preload_info && *preload_info)
for (i = 0; (*preload_info)[i].size; i++)
wine_mmap_add_reserved_area( (*preload_info)[i].addr, (*preload_info)[i].size );
mmap_init( preload_info ? *preload_info : NULL );
}