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, static NTSTATUS map_image_into_view( struct file_view *view, int fd, void *orig_base,
pe_image_info_t *image_info, int shared_fd, BOOL removable, PVOID *addr_ptr ) SIZE_T header_size, ULONG image_flags, int shared_fd, BOOL removable )
{ {
IMAGE_DOS_HEADER *dos; IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS *nt; 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_SECTION_HEADER *sec;
IMAGE_DATA_DIRECTORY *imports; IMAGE_DATA_DIRECTORY *imports;
NTSTATUS status = STATUS_CONFLICTING_ADDRESSES; NTSTATUS status = STATUS_CONFLICTING_ADDRESSES;
SIZE_T header_size, total_size = image_info->map_size;
int i; int i;
off_t pos; off_t pos;
sigset_t sigset;
struct stat st; struct stat st;
struct file_view *view = NULL; char *header_end, *header_start;
char *ptr, *header_end, *header_start; char *ptr = view->base;
char *base = wine_server_get_ptr( image_info->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 ); TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );
/* map the header */ /* map the header */
fstat( fd, &st ); fstat( fd, &st );
header_size = min( image_info->header_size, st.st_size ); header_size = min( header_size, st.st_size );
if ((status = map_pe_header( view->base, header_size, fd, &removable )) != STATUS_SUCCESS) goto error; if ((status = map_pe_header( view->base, header_size, fd, &removable ))) return status;
status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */ status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */
dos = (IMAGE_DOS_HEADER *)ptr; dos = (IMAGE_DOS_HEADER *)ptr;
nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew); nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
header_end = ptr + ROUND_SIZE( 0, header_size ); header_end = ptr + ROUND_SIZE( 0, header_size );
memset( ptr + header_size, 0, header_end - (ptr + 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; header_start = (char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader;
if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( sections )) goto error; if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( sections )) return status;
if (header_start + sizeof(*sections) * nt->FileHeader.NumberOfSections > header_end) goto error; 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, /* 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. */ * copying the headers into local memory is necessary to properly load such applications. */
memcpy(sections, header_start, sizeof(*sections) * nt->FileHeader.NumberOfSections); 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 */ /* 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 */ /* unaligned sections, this happens for native subsystem binaries */
/* in that case Windows simply maps in the whole file */ /* in that case Windows simply maps in the whole file */
total_size = min( total_size, ROUND_SIZE( 0, st.st_size )); 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, 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 */ /* 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++) for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
{ {
if (sec[i].VirtualAddress != sec[i].PointerToRawData) 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 the image protections */
set_vprot( view, ptr, total_size, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC ); set_vprot( view, ptr, total_size, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );
/* no relocations are performed on non page-aligned binaries */ /* 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", WARN_(module)( "Section %.8s too large (%x+%lx/%lx)\n",
sec->Name, sec->VirtualAddress, map_size, total_size ); sec->Name, sec->VirtualAddress, map_size, total_size );
goto error; return status;
} }
if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) && 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) VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS)
{ {
ERR_(module)( "Could not map shared section %.8s\n", sec->Name ); ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
goto error; return status;
} }
/* check if the import directory falls inside this section */ /* 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) removable ) != STATUS_SUCCESS)
{ {
ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name ); ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
goto error; return status;
} }
if (file_size & page_mask) 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 ); 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 #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 #endif
if (ptr != base) return STATUS_IMAGE_NOT_AT_BASE;
return STATUS_SUCCESS; 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; mem_size_t full_size;
ACCESS_MASK access; ACCESS_MASK access;
SIZE_T size; SIZE_T size;
void *base;
int unix_handle = -1, needs_close; int unix_handle = -1, needs_close;
int shared_fd = -1, shared_needs_close = 0;
unsigned int vprot, sec_flags; unsigned int vprot, sec_flags;
struct file_view *view; struct file_view *view;
HANDLE shared_file; HANDLE shared_file;
@ -2271,73 +2226,76 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
SERVER_END_REQ; SERVER_END_REQ;
if (res) return res; if (res) return res;
if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL ))) goto done; if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL )))
if (sec_flags & SEC_IMAGE)
{ {
if (shared_file) if (shared_file) NtClose( shared_file );
{ return res;
int shared_fd, shared_needs_close; }
if ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA, if (shared_file && ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA,
&shared_fd, &shared_needs_close, NULL, NULL ))) goto done; &shared_fd, &shared_needs_close, NULL, NULL ))))
res = map_image( handle, access, unix_handle, alloc_type & MEM_TOP_DOWN, zero_bits_64, image_info, {
shared_fd, needs_close, addr_ptr ); NtClose( shared_file );
if (shared_needs_close) close( shared_fd );
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 (needs_close) close( unix_handle );
if (res >= 0) *size_ptr = image_info->map_size;
return res; return res;
} }
res = STATUS_INVALID_PARAMETER; res = STATUS_INVALID_PARAMETER;
if (offset.QuadPart >= full_size) goto done; server_enter_uninterrupted_section( &csVirtual, &sigset );
if (*size_ptr)
if (sec_flags & SEC_IMAGE)
{ {
size = *size_ptr; base = wine_server_get_ptr( image_info->base );
if (size > full_size - offset.QuadPart) if ((ULONG_PTR)base != image_info->base) base = NULL;
{ size = image_info->map_size;
res = STATUS_INVALID_VIEW_SIZE; vprot = SEC_IMAGE | SEC_FILE | VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY;
goto done;
} 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 else
{ {
size = full_size - offset.QuadPart; base = *addr_ptr;
if (size != full_size - offset.QuadPart) /* truncated */ if (offset.QuadPart >= full_size) goto done;
if (*size_ptr)
{ {
WARN( "Files larger than 4Gb (%s) not supported on this platform\n", size = *size_ptr;
wine_dbgstr_longlong(full_size) ); if (size > full_size - offset.QuadPart)
goto done; {
res = STATUS_INVALID_VIEW_SIZE;
goto done;
}
} }
} else
if (!(size = ROUND_SIZE( 0, size ))) goto done; /* wrap-around */ {
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;
}
}
if (!(size = ROUND_SIZE( 0, size ))) goto done; /* wrap-around */
/* Reserve a properly aligned area */ get_vprot_flags( protect, &vprot, FALSE );
vprot |= sec_flags;
if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED;
res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits_64 );
if (res) goto done;
server_enter_uninterrupted_section( &csVirtual, &sigset ); 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 );
get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE ); if (res) ERR( "mapping %p %lx %x%08x failed\n",
vprot |= sec_flags; view->base, size, offset.u.HighPart, offset.u.LowPart );
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 */
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 == STATUS_SUCCESS) if (res == STATUS_SUCCESS)
{ {
SERVER_START_REQ( map_view ) SERVER_START_REQ( map_view )
@ -2352,22 +2310,19 @@ NTSTATUS CDECL virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned sho
SERVER_END_REQ; SERVER_END_REQ;
} }
if (res == STATUS_SUCCESS) if (res >= 0)
{ {
*addr_ptr = view->base; *addr_ptr = view->base;
*size_ptr = size; *size_ptr = size;
VIRTUAL_DEBUG_DUMP_VIEW( view ); VIRTUAL_DEBUG_DUMP_VIEW( view );
} }
else else delete_view( view );
{
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 );
done: done:
server_leave_uninterrupted_section( &csVirtual, &sigset );
if (needs_close) close( unix_handle ); if (needs_close) close( unix_handle );
if (shared_needs_close) close( shared_fd );
if (shared_file) NtClose( shared_file );
return res; 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->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->committed = mapping->committed ? (struct ranges *)grab_object( mapping->committed ) : NULL;
view->shared = mapping->shared ? (struct shared_map *)grab_object( mapping->shared ) : 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 ); list_add_tail( &current->process->views, &view->entry );
} }

View File

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