Alexandre Julliard <julliard(a)winehq.com> writes:
> Another related problem is that even without this bug, mmap64 will
> always refuse to do an unaligned mmap, even if the kernel might have
> accepted it (with kernel 2.2.x for instance). Maybe the best solution
> would be to directly call the mmap syscall instead of mmap2 when
> mapping executables, this would fix both problems.
OK, here we go. Could you please test this, especially people running
kernel 2.4?
Index: memory/virtual.c
===================================================================
RCS file: /opt/cvs-commit/wine/memory/virtual.c,v
retrieving revision 1.63
diff -u -r1.63 virtual.c
--- memory/virtual.c 2001/06/13 20:13:20 1.63
+++ memory/virtual.c 2001/06/14 02:15:36
@@ -104,7 +104,8 @@
#define VIRTUAL_DEBUG_DUMP_VIEW(view) \
if (!TRACE_ON(virtual)); else VIRTUAL_DumpView(view)
-static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size, DWORD offset, int prot, int flags );
+static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size, DWORD offset_low,
+ DWORD offset_high, int prot, int flags );
/* filter for page-fault exceptions */
static WINE_EXCEPTION_FILTER(page_fault)
@@ -471,7 +472,7 @@
/* map the header */
- if (VIRTUAL_mmap( fd, ptr, header_size, 0, PROT_READ | PROT_WRITE,
+ if (VIRTUAL_mmap( fd, ptr, header_size, 0, 0, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_FIXED ) == (char *)-1) goto error;
dos = (IMAGE_DOS_HEADER *)ptr;
nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
@@ -533,7 +534,7 @@
sec->PointerToRawData, pos, sec->SizeOfRawData,
size, sec->Characteristics );
if (VIRTUAL_mmap( shared_fd, (char *)ptr + sec->VirtualAddress, size,
- pos, PROT_READ|PROT_WRITE|PROT_EXEC,
+ pos, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_SHARED|MAP_FIXED ) == (void *)-1)
{
ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
@@ -555,7 +556,7 @@
* fall back to read(), so we don't need to check anything here.
*/
if (VIRTUAL_mmap( fd, (char *)ptr + sec->VirtualAddress, sec->SizeOfRawData,
- sec->PointerToRawData, PROT_READ|PROT_WRITE|PROT_EXEC,
+ sec->PointerToRawData, 0, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE | MAP_FIXED ) == (void *)-1)
{
ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
@@ -650,6 +651,46 @@
}
+
+/***********************************************************************
+ * unaligned_mmap
+ *
+ * Linux kernels before 2.4.x can support non page-aligned offsets, as
+ * long as the offset is aligned to the filesystem block size. This is
+ * a big performance gain so we want to take advantage of it.
+ *
+ * However, when we use 64-bit file support this doesn't work because
+ * glibc rejects unaligned offsets. Also glibc 2.1.3 mmap64 is broken
+ * in that it rounds unaligned offsets down to a page boundary. For
+ * these reasons we do a direct system call here.
+ */
+static void *unaligned_mmap( void *addr, size_t length, unsigned int prot,
+ unsigned int flags, int fd, unsigned int offset_low,
+ unsigned int offset_high )
+{
+#if defined(linux) && defined(__i386__) && defined(__GNUC__)
+ if (!offset_high && (offset_low & page_mask))
+ {
+ int ret;
+ __asm__ __volatile__("push %%ebx\n\t"
+ "movl %2,%%ebx\n\t"
+ "int $0x80\n\t"
+ "popl %%ebx"
+ : "=a" (ret)
+ : "0" (90), /* SYS_mmap */
+ "g" (&addr) );
+ if (ret < 0 && ret > -4096)
+ {
+ errno = -ret;
+ ret = -1;
+ }
+ return (void *)ret;
+ }
+#endif
+ return mmap( addr, length, prot, flags, fd, ((off_t)offset_high << 32) | offset_low );
+}
+
+
/***********************************************************************
* VIRTUAL_mmap
*
@@ -657,15 +698,16 @@
* and falls back to read if mmap of a file fails.
*/
static LPVOID VIRTUAL_mmap( int fd, LPVOID start, DWORD size,
- DWORD offset, int prot, int flags )
+ DWORD offset_low, DWORD offset_high, int prot, int flags )
{
int pos;
LPVOID ret;
+ off_t offset;
if (fd == -1) return wine_anon_mmap( start, size, prot, flags );
- if ((ret = mmap( start, size, prot, flags, fd, offset )) != (LPVOID)-1)
- return ret;
+ if ((ret = unaligned_mmap( start, size, prot, flags, fd,
+ offset_low, offset_high )) != (LPVOID)-1) return ret;
/* mmap() failed; if this is because the file offset is not */
/* page-aligned (EINVAL), or because the underlying filesystem */
@@ -687,6 +729,7 @@
ret = wine_anon_mmap( start, size, PROT_READ | PROT_WRITE, flags );
if (ret == (LPVOID)-1) return ret;
/* Now read in the file */
+ offset = ((off_t)offset_high << 32) | offset_low;
if ((pos = lseek( fd, offset, SEEK_SET )) == -1)
{
munmap( ret, size );
@@ -1524,8 +1567,8 @@
shared_file, shared_size );
- if (size_high || offset_high)
- ERR("Offsets larger than 4Gb not supported\n");
+ if (size_high)
+ ERR("Sizes larger than 4Gb not supported\n");
if ((offset_low >= size_low) ||
(count > size_low - offset_low))
@@ -1570,7 +1613,7 @@
TRACE("handle=%x size=%x offset=%lx\n", handle, size, offset_low );
- ptr = (UINT)VIRTUAL_mmap( unix_handle, addr, size, offset_low,
+ ptr = (UINT)VIRTUAL_mmap( unix_handle, addr, size, offset_low, offset_high,
VIRTUAL_GetUnixProt( prot ), flags );
if (ptr == (UINT)-1) {
/* KB: Q125713, 25-SEP-1995, "Common File Mapping Problems and
--
Alexandre Julliard
julliard(a)winehq.com