ntdll: Move part of the PE image mapping code into virtual_map_section().

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-06-30 12:00:08 +02:00
parent 64731a8e9f
commit 4fcf20d1d1
3 changed files with 88 additions and 128 deletions

View File

@ -1969,12 +1969,13 @@ static NTSTATUS map_pe_header( void *ptr, size_t size, int fd, BOOL *removable )
/***********************************************************************
* map_image
* map_image_into_view
*
* Map an executable (PE format) image into memory.
* Map an executable (PE format) image into an existing view.
* The csVirtual section must be held by caller.
*/
static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_down, unsigned short zero_bits_64,
pe_image_info_t *image_info, int shared_fd, BOOL removable, PVOID *addr_ptr )
static NTSTATUS map_image_into_view( struct file_view *view, int fd, void *orig_base,
SIZE_T header_size, ULONG image_flags, int shared_fd, BOOL removable )
{
IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS *nt;
@ -1982,54 +1983,30 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
IMAGE_SECTION_HEADER *sec;
IMAGE_DATA_DIRECTORY *imports;
NTSTATUS status = STATUS_CONFLICTING_ADDRESSES;
SIZE_T header_size, total_size = image_info->map_size;
int i;
off_t pos;
sigset_t sigset;
struct stat st;
struct file_view *view = NULL;
char *ptr, *header_end, *header_start;
char *base = wine_server_get_ptr( image_info->base );
char *header_end, *header_start;
char *ptr = view->base;
SIZE_T total_size = view->size;
if (total_size != image_info->map_size) /* truncated */
{
WARN( "Modules larger than 4Gb (%s) not supported\n", wine_dbgstr_longlong(image_info->map_size) );
return STATUS_INVALID_PARAMETER;
}
if ((ULONG_PTR)base != image_info->base) base = NULL;
/* zero-map the whole range */
server_enter_uninterrupted_section( &csVirtual, &sigset );
if (base >= (char *)address_space_start) /* make sure the DOS area remains free */
status = map_view( &view, base, total_size, top_down, SEC_IMAGE | SEC_FILE |
VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, zero_bits_64 );
if (status != STATUS_SUCCESS)
status = map_view( &view, NULL, total_size, top_down, SEC_IMAGE | SEC_FILE |
VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, zero_bits_64 );
if (status != STATUS_SUCCESS) goto error;
ptr = view->base;
TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );
/* map the header */
fstat( fd, &st );
header_size = min( image_info->header_size, st.st_size );
if ((status = map_pe_header( view->base, header_size, fd, &removable )) != STATUS_SUCCESS) goto error;
header_size = min( header_size, st.st_size );
if ((status = map_pe_header( view->base, header_size, fd, &removable ))) return status;
status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */
dos = (IMAGE_DOS_HEADER *)ptr;
nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
header_end = ptr + ROUND_SIZE( 0, header_size );
memset( ptr + header_size, 0, header_end - (ptr + header_size) );
if ((char *)(nt + 1) > header_end) goto error;
if ((char *)(nt + 1) > header_end) return status;
header_start = (char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader;
if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( sections )) goto error;
if (header_start + sizeof(*sections) * nt->FileHeader.NumberOfSections > header_end) goto error;
if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( sections )) return status;
if (header_start + sizeof(*sections) * nt->FileHeader.NumberOfSections > header_end) return status;
/* Some applications (e.g. the Steam version of Borderlands) map over the top of the section headers,
* copying the headers into local memory is necessary to properly load such applications. */
memcpy(sections, header_start, sizeof(*sections) * nt->FileHeader.NumberOfSections);
@ -2040,28 +2017,28 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
/* check for non page-aligned binary */
if (image_info->image_flags & IMAGE_FLAGS_ImageMappedFlat)
if (image_flags & IMAGE_FLAGS_ImageMappedFlat)
{
/* unaligned sections, this happens for native subsystem binaries */
/* in that case Windows simply maps in the whole file */
total_size = min( total_size, ROUND_SIZE( 0, st.st_size ));
if (map_file_into_view( view, fd, 0, total_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
removable ) != STATUS_SUCCESS) goto error;
removable ) != STATUS_SUCCESS) return status;
/* check that all sections are loaded at the right offset */
if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto error;
if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) return status;
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 */
return status; /* Windows refuses to load in that case too */
}
/* set the image protections */
set_vprot( view, ptr, total_size, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );
/* no relocations are performed on non page-aligned binaries */
goto done;
return STATUS_SUCCESS;
}
@ -2088,7 +2065,7 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
{
WARN_(module)( "Section %.8s too large (%x+%lx/%lx)\n",
sec->Name, sec->VirtualAddress, map_size, total_size );
goto error;
return status;
}
if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) &&
@ -2102,7 +2079,7 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS)
{
ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
goto error;
return status;
}
/* check if the import directory falls inside this section */
@ -2140,7 +2117,7 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
removable ) != STATUS_SUCCESS)
{
ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
goto error;
return status;
}
if (file_size & page_mask)
@ -2183,34 +2160,10 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, int top_
sec->Characteristics, sec->Name );
}
done:
SERVER_START_REQ( map_view )
{
req->mapping = wine_server_obj_handle( hmapping );
req->access = access;
req->base = wine_server_client_ptr( view->base );
req->size = view->size;
req->start = 0;
status = wine_server_call( req );
}
SERVER_END_REQ;
if (status) goto error;
VIRTUAL_DEBUG_DUMP_VIEW( view );
server_leave_uninterrupted_section( &csVirtual, &sigset );
*addr_ptr = ptr;
#ifdef VALGRIND_LOAD_PDB_DEBUGINFO
VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - base);
VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - (char *)orig_base);
#endif
if (ptr != base) return STATUS_IMAGE_NOT_AT_BASE;
return STATUS_SUCCESS;
error:
if (view) delete_view( view );
server_leave_uninterrupted_section( &csVirtual, &sigset );
return status;
}
@ -2227,7 +2180,9 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
mem_size_t full_size;
ACCESS_MASK access;
SIZE_T size;
void *base;
int unix_handle = -1, needs_close;
int shared_fd = -1, shared_needs_close = 0;
unsigned int vprot, sec_flags;
struct file_view *view;
HANDLE shared_file;
@ -2271,32 +2226,42 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
SERVER_END_REQ;
if (res) return res;
if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL ))) goto done;
if (sec_flags & SEC_IMAGE)
if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL )))
{
if (shared_file)
{
int shared_fd, shared_needs_close;
if (shared_file) NtClose( shared_file );
return res;
}
if ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA,
&shared_fd, &shared_needs_close, NULL, NULL ))) goto done;
res = map_image( handle, access, unix_handle, alloc_type & MEM_TOP_DOWN, zero_bits_64, image_info,
shared_fd, needs_close, addr_ptr );
if (shared_needs_close) close( shared_fd );
if (shared_file && ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA,
&shared_fd, &shared_needs_close, NULL, NULL ))))
{
NtClose( shared_file );
}
else
{
res = map_image( handle, access, unix_handle, alloc_type & MEM_TOP_DOWN, zero_bits_64, image_info,
-1, needs_close, addr_ptr );
}
if (needs_close) close( unix_handle );
if (res >= 0) *size_ptr = image_info->map_size;
return res;
}
res = STATUS_INVALID_PARAMETER;
server_enter_uninterrupted_section( &csVirtual, &sigset );
if (sec_flags & SEC_IMAGE)
{
base = wine_server_get_ptr( image_info->base );
if ((ULONG_PTR)base != image_info->base) base = NULL;
size = image_info->map_size;
vprot = SEC_IMAGE | SEC_FILE | VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY;
if ((char *)base >= (char *)address_space_start) /* make sure the DOS area remains free */
res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits_64 );
if (res) res = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits_64 );
if (res) goto done;
res = map_image_into_view( view, unix_handle, base, image_info->header_size,
image_info->image_flags, shared_fd, needs_close );
}
else
{
base = *addr_ptr;
if (offset.QuadPart >= full_size) goto done;
if (*size_ptr)
{
@ -2319,25 +2284,18 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
}
if (!(size = ROUND_SIZE( 0, size ))) goto done; /* wrap-around */
/* Reserve a properly aligned area */
server_enter_uninterrupted_section( &csVirtual, &sigset );
get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE );
get_vprot_flags( protect, &vprot, FALSE );
vprot |= sec_flags;
if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED;
res = map_view( &view, *addr_ptr, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits_64 );
if (res)
{
server_leave_uninterrupted_section( &csVirtual, &sigset );
goto done;
}
/* Map the file */
res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits_64 );
if (res) goto done;
TRACE( "handle=%p size=%lx offset=%x%08x\n", handle, size, offset.u.HighPart, offset.u.LowPart );
res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close );
if (res) ERR( "mapping %p %lx %x%08x failed\n",
view->base, size, offset.u.HighPart, offset.u.LowPart );
}
if (res == STATUS_SUCCESS)
{
SERVER_START_REQ( map_view )
@ -2352,22 +2310,19 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
SERVER_END_REQ;
}
if (res == STATUS_SUCCESS)
if (res >= 0)
{
*addr_ptr = view->base;
*size_ptr = size;
VIRTUAL_DEBUG_DUMP_VIEW( view );
}
else
{
ERR( "mapping %p %lx %x%08x failed\n", view->base, size, offset.u.HighPart, offset.u.LowPart );
delete_view( view );
}
server_leave_uninterrupted_section( &csVirtual, &sigset );
else delete_view( view );
done:
server_leave_uninterrupted_section( &csVirtual, &sigset );
if (needs_close) close( unix_handle );
if (shared_needs_close) close( shared_fd );
if (shared_file) NtClose( shared_file );
return res;
}

View File

@ -1075,7 +1075,11 @@ DECL_HANDLER(map_view)
view->fd = !is_fd_removable( mapping->fd ) ? (struct fd *)grab_object( mapping->fd ) : NULL;
view->committed = mapping->committed ? (struct ranges *)grab_object( mapping->committed ) : NULL;
view->shared = mapping->shared ? (struct shared_map *)grab_object( mapping->shared ) : NULL;
if (mapping->flags & SEC_IMAGE) view->image = mapping->image;
if (mapping->flags & SEC_IMAGE)
{
view->image = mapping->image;
if (view->base != mapping->image.base) set_error( STATUS_IMAGE_NOT_AT_BASE );
}
list_add_tail( &current->process->views, &view->entry );
}

View File

@ -5555,6 +5555,7 @@ static const struct
{ "HANDLE_NOT_CLOSABLE", STATUS_HANDLE_NOT_CLOSABLE },
{ "HOST_UNREACHABLE", STATUS_HOST_UNREACHABLE },
{ "ILLEGAL_FUNCTION", STATUS_ILLEGAL_FUNCTION },
{ "IMAGE_NOT_AT_BASE", STATUS_IMAGE_NOT_AT_BASE },
{ "INFO_LENGTH_MISMATCH", STATUS_INFO_LENGTH_MISMATCH },
{ "INSTANCE_NOT_AVAILABLE", STATUS_INSTANCE_NOT_AVAILABLE },
{ "INSUFFICIENT_RESOURCES", STATUS_INSUFFICIENT_RESOURCES },