From efd03f40e6e315d89cd1d09c48180aae82033f9f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 5 Oct 2023 10:41:47 +0200 Subject: [PATCH] server: Assign a system-wide mapping address for dynamic base modules. --- dlls/ntdll/unix/virtual.c | 27 ++++++++- include/wine/server_protocol.h | 19 +++++- server/fd.c | 21 +++++++ server/file.h | 4 ++ server/main.c | 1 + server/mapping.c | 105 ++++++++++++++++++++++++++++++++- server/protocol.def | 9 +++ server/request.h | 8 ++- server/trace.c | 13 ++++ tools/make_requests | 2 +- 10 files changed, 200 insertions(+), 9 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 03e2e96e30d..c017b45fc1e 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2936,7 +2936,7 @@ static NTSTATUS map_image_view( struct file_view **view_ret, pe_image_info_t *im ULONG_PTR limit_low, ULONG_PTR limit_high, ULONG alloc_type ) { unsigned int vprot = SEC_IMAGE | SEC_FILE | VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY; - void *base = wine_server_get_ptr( image_info->base ); + void *base; NTSTATUS status; ULONG_PTR start, end; BOOL top_down = (image_info->image_charact & IMAGE_FILE_DLL) && @@ -2947,9 +2947,18 @@ static NTSTATUS map_image_view( struct file_view **view_ret, pe_image_info_t *im /* first try the specified base */ - if (base && (ULONG_PTR)base == image_info->base) + if (image_info->map_addr) + { + base = wine_server_get_ptr( image_info->map_addr ); + if ((ULONG_PTR)base != image_info->map_addr) base = NULL; + } + else + { + base = wine_server_get_ptr( image_info->base ); + if ((ULONG_PTR)base != image_info->base) base = NULL; + } + if (base) { - if (top_down) alloc_type |= MEM_TOP_DOWN; status = map_view( view_ret, base, size, alloc_type, vprot, limit_low, limit_high, 0 ); if (!status) return status; } @@ -3005,6 +3014,18 @@ static NTSTATUS virtual_map_image( HANDLE mapping, void **addr_ptr, SIZE_T *size return status; } + if (!image_info->map_addr && + (image_info->image_charact & IMAGE_FILE_DLL) && + (image_info->image_flags & IMAGE_FLAGS_ImageDynamicallyRelocated)) + { + SERVER_START_REQ( get_image_map_address ) + { + req->handle = wine_server_obj_handle( mapping ); + if (!wine_server_call( req )) image_info->map_addr = reply->addr; + } + SERVER_END_REQ; + } + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); status = map_image_view( &view, image_info, size, limit_low, limit_high, alloc_type ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 9ad2bf9c3f9..a392b532f4e 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -827,6 +827,7 @@ typedef union typedef struct { client_ptr_t base; + client_ptr_t map_addr; mem_size_t stack_size; mem_size_t stack_commit; unsigned int entry_point; @@ -1997,6 +1998,19 @@ struct get_mapping_info_reply +struct get_image_map_address_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_image_map_address_reply +{ + struct reply_header __header; + client_ptr_t addr; +}; + + + struct map_view_request { struct request_header __header; @@ -5687,6 +5701,7 @@ enum request REQ_create_mapping, REQ_open_mapping, REQ_get_mapping_info, + REQ_get_image_map_address, REQ_map_view, REQ_map_image_view, REQ_map_builtin_view, @@ -5977,6 +5992,7 @@ union generic_request struct create_mapping_request create_mapping_request; struct open_mapping_request open_mapping_request; struct get_mapping_info_request get_mapping_info_request; + struct get_image_map_address_request get_image_map_address_request; struct map_view_request map_view_request; struct map_image_view_request map_image_view_request; struct map_builtin_view_request map_builtin_view_request; @@ -6265,6 +6281,7 @@ union generic_reply struct create_mapping_reply create_mapping_reply; struct open_mapping_reply open_mapping_reply; struct get_mapping_info_reply get_mapping_info_reply; + struct get_image_map_address_reply get_image_map_address_reply; struct map_view_reply map_view_reply; struct map_image_view_reply map_image_view_reply; struct map_builtin_view_reply map_builtin_view_reply; @@ -6487,7 +6504,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 783 +#define SERVER_PROTOCOL_VERSION 784 /* ### protocol_version end ### */ diff --git a/server/fd.c b/server/fd.c index 5947dee3ae1..2785bd54d63 100644 --- a/server/fd.c +++ b/server/fd.c @@ -136,6 +136,8 @@ struct fd struct closed_fd *closed; /* structure to store the unix fd at destroy time */ struct object *user; /* object using this file descriptor */ struct list locks; /* list of locks on this fd */ + client_ptr_t map_addr; /* default mapping address for PE files */ + mem_size_t map_size; /* mapping size for PE files */ unsigned int access; /* file access (FILE_READ_DATA etc.) */ unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */ unsigned int sharing; /* file sharing mode */ @@ -1547,6 +1549,7 @@ static void fd_destroy( struct object *obj ) free_async_queue( &fd->write_q ); free_async_queue( &fd->wait_q ); + if (fd->map_addr) free_map_addr( fd->map_addr, fd->map_size ); if (fd->completion) release_object( fd->completion ); remove_fd_locks( fd ); list_remove( &fd->inode_entry ); @@ -1665,6 +1668,8 @@ static struct fd *alloc_fd_object(void) fd->user = NULL; fd->inode = NULL; fd->closed = NULL; + fd->map_addr = 0; + fd->map_size = 0; fd->access = 0; fd->options = 0; fd->sharing = 0; @@ -1703,6 +1708,8 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use fd->user = user; fd->inode = NULL; fd->closed = NULL; + fd->map_addr = 0; + fd->map_size = 0; fd->access = 0; fd->options = options; fd->sharing = 0; @@ -2095,6 +2102,20 @@ int get_unix_fd( struct fd *fd ) return fd->unix_fd; } +/* retrieve the suggested mapping address for the fd */ +client_ptr_t get_fd_map_address( struct fd *fd ) +{ + return fd->map_addr; +} + +/* set the suggested mapping address for the fd */ +void set_fd_map_address( struct fd *fd, client_ptr_t addr, mem_size_t size ) +{ + assert( !fd->map_addr ); + fd->map_addr = addr; + fd->map_size = size; +} + /* check if two file descriptors point to the same file */ int is_same_file_fd( struct fd *fd1, struct fd *fd2 ) { diff --git a/server/file.h b/server/file.h index a43b4afad08..39a833cd105 100644 --- a/server/file.h +++ b/server/file.h @@ -94,6 +94,8 @@ extern unsigned int get_fd_options( struct fd *fd ); extern unsigned int get_fd_comp_flags( struct fd *fd ); extern int is_fd_overlapped( struct fd *fd ); extern int get_unix_fd( struct fd *fd ); +extern client_ptr_t get_fd_map_address( struct fd *fd ); +extern void set_fd_map_address( struct fd *fd, client_ptr_t addr, mem_size_t size ); extern int is_same_file_fd( struct fd *fd1, struct fd *fd2 ); extern int is_fd_removable( struct fd *fd ); extern int check_fd_events( struct fd *fd, int events ); @@ -172,7 +174,9 @@ extern int is_file_executable( const char *name ); struct memory_view; +extern void init_memory(void); extern int grow_file( int unix_fd, file_pos_t new_size ); +extern void free_map_addr( client_ptr_t base, mem_size_t size ); extern struct memory_view *find_mapped_view( struct process *process, client_ptr_t base ); extern struct memory_view *get_exe_view( struct process *process ); extern struct file *get_view_file( const struct memory_view *view, unsigned int access, unsigned int sharing ); diff --git a/server/main.c b/server/main.c index 4021d55d52c..1248b92f24d 100644 --- a/server/main.c +++ b/server/main.c @@ -232,6 +232,7 @@ int main( int argc, char *argv[] ) if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() ); set_current_time(); init_signals(); + init_memory(); init_directories( load_intl_file() ); init_registry(); main_loop(); diff --git a/server/mapping.c b/server/mapping.c index a6adf4dfbe2..41585b35924 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -208,10 +208,31 @@ static const struct fd_ops mapping_fd_ops = default_fd_reselect_async /* reselect_async */ }; +/* free address ranges for PE image mappings */ +struct addr_range +{ + unsigned int count; + unsigned int size; + struct + { + client_ptr_t base; + mem_size_t size; + } *free; +}; + static size_t page_mask; +static const mem_size_t granularity_mask = 0xffff; +static struct addr_range ranges32; +static struct addr_range ranges64; #define ROUND_SIZE(size) (((size) + page_mask) & ~page_mask) +void init_memory(void) +{ + page_mask = sysconf( _SC_PAGESIZE ) - 1; + free_map_addr( 0x60000000, 0x1c000000 ); + free_map_addr( 0x600000000000, 0x100000000000 ); +} static void ranges_dump( struct object *obj, int verbose ) { @@ -826,6 +847,7 @@ static unsigned int get_image_params( struct mapping *mapping, file_pos_t file_s return STATUS_INVALID_IMAGE_FORMAT; } + mapping->image.map_addr = get_fd_map_address( mapping->fd ); mapping->image.image_charact = nt.FileHeader.Characteristics; mapping->image.machine = nt.FileHeader.Machine; mapping->image.dbg_offset = nt.FileHeader.PointerToSymbolTable; @@ -919,8 +941,6 @@ static struct mapping *create_mapping( struct object *root, const struct unicode int unix_fd; struct stat st; - if (!page_mask) page_mask = sysconf( _SC_PAGESIZE ) - 1; - if (!(mapping = create_named_object( root, &mapping_ops, name, attr, sd ))) return NULL; if (get_error() == STATUS_OBJECT_NAME_EXISTS) @@ -1137,9 +1157,70 @@ static enum server_fd_type mapping_get_fd_type( struct fd *fd ) return FD_TYPE_FILE; } +/* assign a mapping address to a PE image mapping */ +static client_ptr_t assign_map_address( struct mapping *mapping ) +{ + unsigned int i; + client_ptr_t ret; + struct addr_range *range = (mapping->image.base >> 32) ? &ranges64 : &ranges32; + mem_size_t size = (mapping->size + granularity_mask) & ~granularity_mask; + + if (!(mapping->image.image_charact & IMAGE_FILE_DLL)) return 0; + + if ((ret = get_fd_map_address( mapping->fd ))) return ret; + + for (i = 0; i < range->count; i++) + { + if (range->free[i].size < size) continue; + range->free[i].size -= size; + ret = range->free[i].base + range->free[i].size; + set_fd_map_address( mapping->fd, ret, size ); + return ret; + } + return 0; +} + +/* free a PE mapping address range when the last mapping is closed */ +void free_map_addr( client_ptr_t base, mem_size_t size ) +{ + unsigned int i; + client_ptr_t end = base + size; + struct addr_range *range = (base >> 32) ? &ranges64 : &ranges32; + + for (i = 0; i < range->count; i++) + { + if (range->free[i].base > end) continue; + if (range->free[i].base + range->free[i].size < base) break; + if (range->free[i].base == end) + { + if (i + 1 < range->count && range->free[i + 1].base + range->free[i + 1].size == base) + { + size += range->free[i].size; + range->count--; + memmove( &range->free[i], &range->free[i + 1], (range->count - i) * sizeof(*range->free) ); + } + else range->free[i].base = base; + } + range->free[i].size += size; + return; + } + + if (range->count == range->size) + { + unsigned int new_size = max( 256, range->size * 2 ); + void *new_free = realloc( range->free, new_size * sizeof(*range->free) ); + if (!new_free) return; + range->size = new_size; + range->free = new_free; + } + memmove( &range->free[i + 1], &range->free[i], (range->count - i) * sizeof(*range->free) ); + range->free[i].base = base; + range->free[i].size = size; + range->count++; +} + int get_page_size(void) { - if (!page_mask) page_mask = sysconf( _SC_PAGESIZE ) - 1; return page_mask + 1; } @@ -1233,6 +1314,24 @@ DECL_HANDLER(get_mapping_info) release_object( mapping ); } +/* get the address to use to map an image mapping */ +DECL_HANDLER(get_image_map_address) +{ + struct mapping *mapping; + + if (!(mapping = get_mapping_obj( current->process, req->handle, SECTION_MAP_READ ))) return; + + if ((mapping->flags & SEC_IMAGE) && + (mapping->image.image_flags & IMAGE_FLAGS_ImageDynamicallyRelocated)) + { + if (!mapping->image.map_addr) mapping->image.map_addr = assign_map_address( mapping ); + reply->addr = mapping->image.map_addr; + } + else set_error( STATUS_INVALID_PARAMETER ); + + release_object( mapping ); +} + /* add a memory view in the current process */ DECL_HANDLER(map_view) { diff --git a/server/protocol.def b/server/protocol.def index 4d2a95d6324..e9195df6b65 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -843,6 +843,7 @@ typedef union typedef struct { client_ptr_t base; + client_ptr_t map_addr; mem_size_t stack_size; mem_size_t stack_commit; unsigned int entry_point; @@ -1612,6 +1613,14 @@ enum server_fd_type @END +/* Get the address to use to map an image mapping */ +@REQ(get_image_map_address) + obj_handle_t handle; /* handle to the mapping */ +@REPLY + client_ptr_t addr; /* map address */ +@END + + /* Add a memory view in the current process */ @REQ(map_view) obj_handle_t mapping; /* file mapping handle */ diff --git a/server/request.h b/server/request.h index 41d38e4347f..d6043c5fdc3 100644 --- a/server/request.h +++ b/server/request.h @@ -184,6 +184,7 @@ DECL_HANDLER(read_change); DECL_HANDLER(create_mapping); DECL_HANDLER(open_mapping); DECL_HANDLER(get_mapping_info); +DECL_HANDLER(get_image_map_address); DECL_HANDLER(map_view); DECL_HANDLER(map_image_view); DECL_HANDLER(map_builtin_view); @@ -473,6 +474,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_create_mapping, (req_handler)req_open_mapping, (req_handler)req_get_mapping_info, + (req_handler)req_get_image_map_address, (req_handler)req_map_view, (req_handler)req_map_image_view, (req_handler)req_map_builtin_view, @@ -717,7 +719,7 @@ C_ASSERT( sizeof(mem_size_t) == 8 ); C_ASSERT( sizeof(message_data_t) == 56 ); C_ASSERT( sizeof(mod_handle_t) == 8 ); C_ASSERT( sizeof(obj_handle_t) == 4 ); -C_ASSERT( sizeof(pe_image_info_t) == 80 ); +C_ASSERT( sizeof(pe_image_info_t) == 88 ); C_ASSERT( sizeof(process_id_t) == 4 ); C_ASSERT( sizeof(property_data_t) == 16 ); C_ASSERT( sizeof(rectangle_t) == 16 ); @@ -1143,6 +1145,10 @@ C_ASSERT( FIELD_OFFSET(struct get_mapping_info_reply, flags) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_mapping_info_reply, shared_file) == 20 ); C_ASSERT( FIELD_OFFSET(struct get_mapping_info_reply, total) == 24 ); C_ASSERT( sizeof(struct get_mapping_info_reply) == 32 ); +C_ASSERT( FIELD_OFFSET(struct get_image_map_address_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_image_map_address_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_image_map_address_reply, addr) == 8 ); +C_ASSERT( sizeof(struct get_image_map_address_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct map_view_request, mapping) == 12 ); C_ASSERT( FIELD_OFFSET(struct map_view_request, access) == 16 ); C_ASSERT( FIELD_OFFSET(struct map_view_request, base) == 24 ); diff --git a/server/trace.c b/server/trace.c index b968b74034d..55ccefa1746 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2199,6 +2199,16 @@ static void dump_get_mapping_info_reply( const struct get_mapping_info_reply *re dump_varargs_unicode_str( ", name=", cur_size ); } +static void dump_get_image_map_address_request( const struct get_image_map_address_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_image_map_address_reply( const struct get_image_map_address_reply *req ) +{ + dump_uint64( " addr=", &req->addr ); +} + static void dump_map_view_request( const struct map_view_request *req ) { fprintf( stderr, " mapping=%04x", req->mapping ); @@ -4662,6 +4672,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_mapping_request, (dump_func)dump_open_mapping_request, (dump_func)dump_get_mapping_info_request, + (dump_func)dump_get_image_map_address_request, (dump_func)dump_map_view_request, (dump_func)dump_map_image_view_request, (dump_func)dump_map_builtin_view_request, @@ -4948,6 +4959,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_mapping_reply, (dump_func)dump_open_mapping_reply, (dump_func)dump_get_mapping_info_reply, + (dump_func)dump_get_image_map_address_reply, NULL, NULL, NULL, @@ -5234,6 +5246,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "create_mapping", "open_mapping", "get_mapping_info", + "get_image_map_address", "map_view", "map_image_view", "map_builtin_view", diff --git a/tools/make_requests b/tools/make_requests index 5303ff058df..77b5ab331f4 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -58,7 +58,7 @@ my %formats = "cursor_pos_t" => [ 24, 8 ], "debug_event_t" => [ 160, 8 ], "message_data_t" => [ 56, 8 ], - "pe_image_info_t" => [ 80, 8 ], + "pe_image_info_t" => [ 88, 8 ], "property_data_t" => [ 16, 8 ], "select_op_t" => [ 264, 8 ], "startup_info_t" => [ 96, 4 ],