exec-shield workaround take 2

Mike McCormack mike at codeweavers.com
Sat Apr 10 08:00:09 CDT 2004


Hi,

This patch works around the exec-shield problems with Fedora Core.  I'd 
be greatful if any people using Fedora core could test it out and see 
how it works.

It should be enough to apply it to the latest CVS tip and recompile.

All comments and flames accepted... /me wears his asbestos suit.

Mike


ChangeLog:
* workaround exec-shield by reserving memory before run ld-linux.so.2

-------------- next part --------------
Index: dlls/kernel/process.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/process.c,v
retrieving revision 1.50
diff -u -r1.50 process.c
--- dlls/kernel/process.c	25 Mar 2004 23:41:04 -0000	1.50
+++ dlls/kernel/process.c	10 Apr 2004 12:01:01 -0000
@@ -1055,7 +1055,8 @@
  *
  * Build the environment of a new child process.
  */
-static char **build_envp( const WCHAR *envW, const WCHAR *extra_envW )
+static char **build_envp( const WCHAR *envW, const WCHAR *extra_envW,
+                          const char *workdir )
 {
     const WCHAR *p;
     char **envp;
@@ -1078,7 +1079,7 @@
     if (!(env = malloc( length ))) return NULL;
     WideCharToMultiByte( CP_UNIXCP, 0, envW, p - envW, env, length, NULL, NULL );
 
-    count += 4;
+    count += 5;
 
     if ((envp = malloc( count * sizeof(*envp) )))
     {
@@ -1093,6 +1094,10 @@
         if ((p = getenv("TMP")))  *envptr++ = alloc_env_string( "TMP=", p );
         if ((p = getenv("HOME"))) *envptr++ = alloc_env_string( "HOME=", p );
         if ((p = getenv("WINEPREFIX"))) *envptr++ = alloc_env_string( "WINEPREFIX=", p );
+
+        /* record the unix working directory */
+        if (workdir) *envptr++ = alloc_env_string( "WINEWORKDIR=", workdir );
+
         /* now put the Windows environment strings */
         for (p = env; *p; p += strlen(p) + 1)
         {
@@ -1131,7 +1136,7 @@
     if (!(pid = fork()))  /* child */
     {
         char **argv = build_argv( cmdline, 0 );
-        char **envp = build_envp( env, NULL );
+        char **envp = build_envp( env, NULL, NULL );
         close( fd[0] );
 
         /* Reset signals that we previously set to SIG_IGN */
@@ -1263,7 +1268,16 @@
     if (!(pid = fork()))  /* child */
     {
         char **argv = build_argv( cmd_line, 1 );
-        char **envp = build_envp( env, extra_env );
+        char **envp;
+
+        /* preseve the current directory, the preloader may forget it */
+        if( !unixdir )
+        {
+            char *dir = HeapAlloc( GetProcessHeap(), 0, MAX_PATH );
+            getcwd( dir, MAX_PATH );
+            unixdir = dir;
+        }
+        envp = build_envp( env, extra_env, unixdir );
 
         close( startfd[1] );
         close( execfd[0] );
@@ -1275,8 +1289,6 @@
         /* Reset signals that we previously set to SIG_IGN */
         signal( SIGPIPE, SIG_DFL );
         signal( SIGCHLD, SIG_DFL );
-
-        if (unixdir) chdir(unixdir);
 
         if (argv && envp)
         {
Index: libs/wine/config.c
===================================================================
RCS file: /home/wine/wine/libs/wine/config.c,v
retrieving revision 1.3
diff -u -r1.3 config.c
--- libs/wine/config.c	11 Nov 2003 22:21:29 -0000	1.3
+++ libs/wine/config.c	10 Apr 2004 12:01:01 -0000
@@ -243,6 +243,23 @@
     return user_name;
 }
 
+/* run wine binary in it's own directory, so it can find it's preloader */
+int wine_cwd_execve( const char *name, char **argv, char **envp )
+{
+    char *dir, *p;
+
+    dir = strdup( name );
+    p = strrchr( dir, '/' );
+    if( p )
+    {
+        *p++ = 0;
+        chdir( dir );
+        name = p;
+    }
+
+    return execve( name, argv, envp );
+}
+
 /* exec a wine internal binary (either the wine loader or the wine server) */
 /* if name is null, default to the name of the current binary */
 void wine_exec_wine_binary( const char *name, char **argv, char **envp )
@@ -257,7 +274,7 @@
     argv[0] = xmalloc( sizeof(BINDIR "/") + strlen(name) );
     strcpy( argv[0], BINDIR "/" );
     strcat( argv[0], name );
-    execve( argv[0], argv, envp );
+    wine_cwd_execve( argv[0], argv, envp );
     free( argv[0] );
 
     /* now try the path of argv0 of the current binary */
@@ -266,7 +283,7 @@
         argv[0] = xmalloc( strlen(argv0_path) + strlen(name) + 1 );
         strcpy( argv[0], argv0_path );
         strcat( argv[0], name );
-        execve( argv[0], argv, envp );
+        wine_cwd_execve( argv[0], argv, envp );
         free( argv[0] );
     }
 
@@ -283,7 +300,7 @@
             memcpy( argv[0], pos, ptr - pos );
             strcpy( argv[0] + (ptr - pos), "/" );
             strcat( argv[0] + (ptr - pos), name );
-            execve( argv[0], argv, envp );
+            wine_cwd_execve( argv[0], argv, envp );
             pos = ptr;
         }
         free( argv[0] );
Index: loader/Makefile.in
===================================================================
RCS file: /home/wine/wine/loader/Makefile.in,v
retrieving revision 1.14
diff -u -r1.14 Makefile.in
--- loader/Makefile.in	22 Nov 2003 00:08:26 -0000	1.14
+++ loader/Makefile.in	10 Apr 2004 12:01:01 -0000
@@ -23,20 +23,30 @@
 LIBPTHREAD  = @LIBPTHREAD@
 LDEXECFLAGS = @LDEXECFLAGS@
 
-wine-glibc: glibc.o Makefile.in
+WINELDEXECFLAGS = $(LDEXECFLAGS),--dynamic-linker=$(WINELDSO)
+
+WINELDSO=ld-winepreload.so
+
+$(WINELDSO): preload.o Makefile.in
+	$(CC) -nostartfiles -lc -static -Wl,"-Ttext=0x7ff81000" -o $@ $<
+
+preload.c: preload.h
+
+wine-glibc: glibc.o Makefile.in $(WINELDSO)
 	$(CC) -o $@ $(LDEXECFLAGS) glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
 
-wine-kthread: $(KTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS)
+wine-kthread: $(KTHREAD_OBJS) Makefile.in $(WINELDSO)
+	$(CC) -o $@ $(WINELDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS)
 
-wine-pthread: $(PTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
+wine-pthread: $(PTHREAD_OBJS) Makefile.in $(WINELDSO)
+	$(CC) -o $@ $(WINELDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
 
 $(MODULE): $(MAIN_BINARY)
 	$(RM) $(MODULE) && $(LN_S) $(MAIN_BINARY) $(MODULE)
 
 install:: $(WINE_BINARIES)
 	$(MKINSTALLDIRS) $(bindir)
+	$(INSTALL_PROGRAM) $(WINELDSO) $(bindir); \
 	for f in $(WINE_BINARIES); do \
 	  if [ "$(MAIN_BINARY)" = "$$f" ]; \
 	  then $(INSTALL_PROGRAM) $$f $(bindir)/$(MODULE); \
@@ -48,6 +58,6 @@
 	$(RM) $(WINE_BINARIES:%=$(bindir)/%) $(bindir)/$(MODULE)
 
 clean::
-	$(RM) $(WINE_BINARIES) $(MODULE)
+	$(RM) $(WINE_BINARIES) $(MODULE) $(WINELDSO)
 
 ### Dependencies:
Index: loader/glibc.c
===================================================================
RCS file: /home/wine/wine/loader/glibc.c,v
retrieving revision 1.3
diff -u -r1.3 glibc.c
--- loader/glibc.c	20 Feb 2004 20:19:23 -0000	1.3
+++ loader/glibc.c	10 Apr 2004 12:01:01 -0000
@@ -75,6 +75,17 @@
 {
     const char *loader = getenv( "WINELOADER" );
     const char *threads = get_threading();
+    char cwd[MAX_PATH], *cwd_var;
+
+    if( !getcwd( cwd, sizeof cwd ) )
+    {
+        fprintf( stderr, "wine: could get current directory\n" );
+        exit(1);
+    }
+    cwd_var = xmalloc( sizeof("WINEWORKDIR=") + strlen(cwd) );
+    strcpy( cwd_var, "WINEWORKDIR=" );
+    strcat( cwd_var, cwd );
+    putenv( cwd_var );
 
     if (loader)
     {
@@ -86,6 +97,9 @@
 
         new_name = xmalloc( (path - loader) + strlen(threads) + 1 );
         memcpy( new_name, loader, path - loader );
+        new_name[ path - loader ] = 0;
+        if( new_name[0] )
+            chdir( new_name );
         strcpy( new_name + (path - loader), threads );
 
         /* update WINELOADER with the new name */
Index: loader/main.c
===================================================================
RCS file: /home/wine/wine/loader/main.c,v
retrieving revision 1.90
diff -u -r1.90 main.c
--- loader/main.c	20 Jan 2004 00:28:01 -0000	1.90
+++ loader/main.c	10 Apr 2004 12:01:01 -0000
@@ -20,6 +20,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include "wine/library.h"
 
 /**********************************************************************
@@ -27,12 +28,13 @@
  */
 int main( int argc, char *argv[] )
 {
-    char error[1024];
+    char error[1024], *dir;
 
-#if 0
-    static char pe_load[256*1024*1024] __attribute__((aligned(4096)));
-    wine_set_pe_load_area( pe_load, sizeof(pe_load) );
-#endif
+    wine_set_pe_load_area((void*)0x00400000, 0x01000000);
+
+    dir = getenv( "WINEWORKDIR" );
+    if( dir )
+        chdir( dir );
 
     wine_init( argc, argv, error, sizeof(error) );
     fprintf( stderr, "wine: failed to initialize: %s\n", error );
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ loader/preload.c	2004-04-10 21:43:23.000000000 +0900
@@ -0,0 +1,570 @@
+/* 
+   mini loader for ld.so  - x86 Linux 2.4.x+ kernels only
+ 
+   Copyright 2004 Mike McCormack for Codeweavers
+ 
+   Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA. 
+ */
+
+/*
+ * Design notes
+ *
+ * The goal of this program is to be a workaround for exec-shield, as used
+ *  by the Linux kernel distributed with Fedora Core and other distros.
+ *
+ * To do this, we implement our own shared object loader that reserves memory
+ *  that is important to Wine, and then loads the the standard shared object
+ *  loader (ELF interpreter), usually ld-linux.so.2
+ *
+ * As Wine is i386 specific, we are not too worried about portability issues,
+ *  but will do our best to keep the system dependencies (syscalls) seperate to 
+ *  the rest (ELF loader and printf) part of the program.
+ *
+ * This program will be invoked as follows:
+ * 
+ *  wld <full path to ELF executable to be loaded> <args...>
+ *
+ * We will try to set up the stack an memory area so that the program that
+ *  loads after us (eg. the wine binary) never knows we were here, except that
+ *  areas of memory it needs are already magically reserved.
+ *
+ * The following memory areas are important to Wine:
+ *  0x00000000 - the DOS area
+ *  0x00400000 - the standard Win32 load address
+ *  0x65430000 - the shared heap at
+ *
+ * There are a few things that will be different after this program passes
+ * control of execution to the loaded binary:
+ *  1) The _ variable in the environment (will be the name of this exe)
+ *  2) The brk area returned by brk(2) 
+ *  3) This program will still be mapped into memory.
+ *  4) The command line kept by the kernel in kernel memory (/proc/self/cmdline)
+ *
+ */
+
+/*
+ * References (things I consulted to understand how ELF loading works):
+ *
+ * glibc 2.3.2   elf/dl-load.c
+ *  http://www.gnu.org/directory/glibc.html
+ *
+ * Linux 2.6.4   fs/binfmt_elf.c
+ *  ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.4.tar.bz2
+ *
+ * Userland exec, by <grugq at hcunix.net>
+ *  http://cert.uni-stuttgart.de/archive/bugtraq/2004/01/msg00002.html
+ *
+ * The ELF specification:
+ *  http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/book387.html
+ */
+
+#include "preload.h"
+#include <elf.h>
+
+/* debugging */
+#undef DUMP_SEGMENTS
+#undef DUMP_ARG
+#undef DUMP_AUX_INFO
+
+unsigned int GL(dl_pagesize) = 0x0000;
+
+struct link_map {
+    ElfW(Addr) l_addr;
+    ElfW(Dyn) *l_ld;
+    const ElfW(Phdr) *l_phdr;
+    ElfW(Addr) l_entry;
+    ElfW(Half) l_ldnum;
+    ElfW(Half) l_phnum;
+    ElfW(Addr) l_map_start, l_map_end;
+};
+
+/*
+ * wld_printf - just the basics 
+ *
+ *  %x prints a hex number
+ *  %s prints a string
+ */
+void wld_sprintf(char *str, char *fmt, int *args )
+{
+    char *p = fmt;
+
+    while( *p )
+    {
+        if( *p == '%' )
+        {
+            p++;
+            if( *p == 'x' )
+            {
+                int ch, i, x = *++args;
+                for(i=7; i>=0; i--)
+                {
+                    ch = (x>>(i*4))&0xf;
+                    ch += '0';
+                    if(ch>'9')
+                        ch+=('A'-10-'0');
+                    *str++ = ch;
+                }
+            }
+            else if( *p == 's' )
+            {
+                char *s = (char*)*++args;
+                while(*s)
+                    *str++ = *s++;
+            }
+            else if( *p == 0 )
+                break;
+            p++;
+        }
+        *str++ = *p++;
+    }
+    *str = 0;
+}
+
+int wld_strlen(char *str)
+{
+    int n = 0;
+    while( str[n] )
+        n++;
+    return n;
+} 
+
+void wld_printf(char *fmt, ... )
+{
+    char buffer[100];
+    int n;
+
+    buffer[0]=0;
+    wld_sprintf(buffer, fmt, (int*)&fmt );
+    n = wld_strlen( buffer );
+    write(2, buffer, n);
+}
+
+void wld_strncpy(char *dest, char *src, int count )
+{
+    while( (*dest++ = *src++) && --count )
+        ;
+    *dest = 0;
+}
+
+void wld_memset( unsigned char *buf, int val, size_t len )
+{
+    while( len-- )
+        *buf++=val;
+}
+
+/*
+ *  Dump interesting bits of the ELF auxv_t structure that is passed
+ *   as the 4th parameter to the _start function
+ */
+void wld_dump_auxilary( ElfW(auxv_t) *av )
+{
+  for (  ; av->a_type != AT_NULL; av++)
+    switch (av->a_type)
+      {
+      case AT_PAGESZ:
+        GL(dl_pagesize) = av->a_un.a_val;
+#ifndef DUMP_AUX_INFO
+        break;
+#else
+        wld_printf("AT_PAGESZ = %x\n",av->a_un.a_val);
+        break;
+      case AT_PHDR:
+        wld_printf("AT_PHDR = %x\n",av->a_un.a_ptr);
+        break;
+      case AT_PHNUM:
+        wld_printf("AT_PHNUM = %x\n",av->a_un.a_val);
+        break;
+      case AT_ENTRY:
+        wld_printf("AT_ENTRY = %x\n",av->a_un.a_val);
+        break;
+      case AT_BASE:
+        wld_printf("AT_BASE = %x\n",av->a_un.a_val);
+        break;
+#endif
+      }
+}
+
+/*
+ * wld_set_auxilary
+ *
+ * Set a field of the auxillary structure
+ */
+void wld_set_auxilary( ElfW(auxv_t) *av, int type, long int val )
+{
+  for ( ; av->a_type != AT_NULL; av++)
+    if( av->a_type == type )
+      av->a_un.a_val = val;
+}
+
+/*
+ * wld_map_so
+ *
+ * modelled after _dl_map_object_from_fd() from glibc-2.3.1/elf/dl-load.c
+ *
+ * This function maps the segments from an ELF object, and optionally
+ *  stores information about the mapping into the auxv_t structure.
+ */
+void* wld_map_so( int fd, void* buf, size_t buflen, ElfW(auxv_t)* av, struct link_map *l)
+{
+    ElfW(Ehdr) *header = buf;
+    ElfW(Phdr) *phdr, *ph;
+    /* Scan the program header table, collecting its load commands.  */
+    struct loadcmd
+      {
+        ElfW(Addr) mapstart, mapend, dataend, allocend;
+        off_t mapoff;
+        int prot;
+      } loadcmds[header->e_phnum], *c;
+    size_t nloadcmds = 0, maplength;
+
+    header = buf;
+    phdr = (void*) (((unsigned char*)buf) + header->e_phoff);
+
+    if( header->e_ident[0] != 0x7f &&
+        header->e_ident[0] != 'E' &&
+        header->e_ident[0] != 'L' &&
+        header->e_ident[0] != 'F' )
+    {
+        wld_printf("Not an ELF binary... don't know how to load it\n");
+        return NULL;
+    }
+
+    if( header->e_machine != EM_386 )
+    {
+        wld_printf("Not an i386 ELF binary... don't know how to load it\n");
+        return NULL;
+    }
+
+    maplength = header->e_phnum * sizeof (ElfW(Phdr));
+    if (header->e_phoff + maplength > (size_t) buflen)
+    {
+        wld_printf("oops... not enough space for ELF headers\n");
+        exit(1);
+    }
+
+    l->l_ld = 0;
+    l->l_addr = 0;
+    l->l_phnum = header->e_phnum;
+    l->l_entry = header->e_entry;
+
+    for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
+    {
+
+#ifdef DUMP_SEGMENTS
+      wld_printf( "ph = %x\n", ph );
+      wld_printf( " p_type   = %x\n", ph->p_type );
+      wld_printf( " p_flags  = %x\n", ph->p_flags );
+      wld_printf( " p_offset = %x\n", ph->p_offset );
+      wld_printf( " p_vaddr  = %x\n", ph->p_vaddr );
+      wld_printf( " p_paddr  = %x\n", ph->p_paddr );
+      wld_printf( " p_filesz = %x\n", ph->p_filesz );
+      wld_printf( " p_memsz  = %x\n", ph->p_memsz );
+      wld_printf( " p_align  = %x\n", ph->p_align );
+#endif
+
+      switch (ph->p_type)
+        {
+          /* These entries tell us where to find things once the file's
+             segments are mapped in.  We record the addresses it says
+             verbatim, and later correct for the run-time load address.  */
+        case PT_DYNAMIC:
+          l->l_ld = (void *) ph->p_vaddr;
+          l->l_ldnum = ph->p_memsz / sizeof (Elf32_Dyn);
+          break;
+
+        case PT_PHDR:
+          l->l_phdr = (void *) ph->p_vaddr;
+          break;
+
+        case PT_TLS:
+          /*
+           * We don't need to set anything up because we're
+           * emulating the kernel, not ld-linux.so.2
+           * The ELF loader will set up the TLS data itself.
+           */
+          break;
+
+        case PT_LOAD:
+          {
+            if ((ph->p_align & (GL(dl_pagesize) - 1)) != 0)
+            {
+              wld_printf("ELF load command alignment not page-aligned");
+              return NULL;
+            }
+            if (((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1)) != 0)
+            {
+              wld_printf("ELF load command address/offset not properly aligned");
+              return NULL;
+            }
+ 
+            c = &loadcmds[nloadcmds++];
+            c->mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+            c->mapend = ((ph->p_vaddr + ph->p_filesz + GL(dl_pagesize) - 1)
+                         & ~(GL(dl_pagesize) - 1));
+            c->dataend = ph->p_vaddr + ph->p_filesz;
+            c->allocend = ph->p_vaddr + ph->p_memsz;
+            c->mapoff = ph->p_offset & ~(ph->p_align - 1);
+
+            c->prot = 0;
+            if (ph->p_flags & PF_R)
+              c->prot |= PROT_READ;
+            if (ph->p_flags & PF_W)
+              c->prot |= PROT_WRITE;
+            if (ph->p_flags & PF_X)
+              c->prot |= PROT_EXEC;
+          }
+          break;
+
+        case PT_INTERP:
+          break;
+
+        case PT_SHLIB:
+        case PT_NOTE:
+        case PT_GNU_EH_FRAME:
+        case 0x6474e551:  /* FIXME: what is this? */
+          break;
+
+        default:
+          wld_printf("warning: unknown header type %x\n", ph->p_type);
+          break;
+        }
+    }
+
+    /* Now process the load commands and map segments into memory.  */
+    c = loadcmds;
+
+    /* Length of the sections to be loaded.  */
+    maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;
+
+    if( header->e_type == ET_DYN )
+    {
+        ElfW(Addr) mappref;
+        mappref = (ELF_PREFERRED_ADDRESS (loader, maplength, c->mapstart)
+                   - MAP_BASE_ADDR (l));
+
+        /* Remember which part of the address space this object uses.  */
+        l->l_map_start = (ElfW(Addr)) mmap ((void *) mappref, maplength,
+                                              c->prot, MAP_COPY | MAP_FILE,
+                                              fd, c->mapoff);
+        /* wld_printf("set  : offset = %x\n", c->mapoff); */
+        /* wld_printf("l->l_map_start = %x\n", l->l_map_start); */
+
+        l->l_map_end = l->l_map_start + maplength;
+        l->l_addr = l->l_map_start - c->mapstart;
+
+        mprotect ((caddr_t) (l->l_addr + c->mapend), 
+                    loadcmds[nloadcmds - 1].allocend - c->mapend,
+                    PROT_NONE);
+        goto postmap;
+    }
+    else
+    {
+        ELF_FIXED_ADDRESS (loader, c->mapstart);
+    }
+
+    /* Remember which part of the address space this object uses.  */
+    l->l_map_start = c->mapstart + l->l_addr;
+    l->l_map_end = l->l_map_start + maplength;
+
+    while (c < &loadcmds[nloadcmds])
+      {
+        if (c->mapend > c->mapstart)
+            /* Map the segment contents from the file.  */
+            mmap ((void *) (l->l_addr + c->mapstart),
+                        c->mapend - c->mapstart, c->prot,
+                        MAP_FIXED | MAP_COPY | MAP_FILE, fd, c->mapoff);
+
+      postmap:
+        if (l->l_phdr == 0
+            && (ElfW(Off)) c->mapoff <= header->e_phoff
+            && ((size_t) (c->mapend - c->mapstart + c->mapoff)
+                >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
+          /* Found the program header in this segment.  */
+          l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);
+
+        if (c->allocend > c->dataend)
+          {
+            /* Extra zero pages should appear at the end of this segment,
+               after the data mapped from the file.   */
+            ElfW(Addr) zero, zeroend, zeropage;
+
+            zero = l->l_addr + c->dataend;
+            zeroend = l->l_addr + c->allocend;
+            zeropage = ((zero + GL(dl_pagesize) - 1)
+                        & ~(GL(dl_pagesize) - 1));
+
+            /*
+             * This is different to the dl-load load...
+             *  ld-linux.so.2 relies on the whole page being zero'ed
+             */
+            zeroend = (zeroend +GL(dl_pagesize) - 1) & ~(GL(dl_pagesize) - 1);
+
+            if (zeroend < zeropage)
+            {
+              /* All the extra data is in the last page of the segment.
+                 We can just zero it.  */
+              zeropage = zeroend;
+            }
+
+            if (zeropage > zero)
+              {
+                /* Zero the final part of the last page of the segment.  */
+                if ((c->prot & PROT_WRITE) == 0)
+                  {
+                    /* Dag nab it.  */
+                    mprotect ((caddr_t) (zero & ~(GL(dl_pagesize) - 1)),
+                                           GL(dl_pagesize),
+                                           c->prot|PROT_WRITE);
+                  }
+                memset ((void *) zero, '\0', zeropage - zero);
+                if ((c->prot & PROT_WRITE) == 0)
+                  mprotect ((caddr_t) (zero & ~(GL(dl_pagesize) - 1)),
+                              GL(dl_pagesize), c->prot);
+              }
+
+            if (zeroend > zeropage)
+              {
+                /* Map the remaining zero pages in from the zero fill FD.  */
+                caddr_t mapat;
+                mapat = mmap ((caddr_t) zeropage, zeroend - zeropage,
+                                c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
+                                ANONFD, 0);
+              }
+          }
+
+        ++c;
+      }
+
+    if (l->l_phdr == NULL)
+    {
+      wld_printf("no program header\n");
+      exit(1);
+    }
+
+    (ElfW(Addr)) l->l_phdr += l->l_addr;
+    (ElfW(Addr)) l->l_entry += l->l_addr;
+
+    return (void*)l->l_entry;
+}
+
+/*
+ *  wld_reserve_wine_memory
+ *
+ *  This is the reason we are here.
+ *  We want to reserve address space so that the kernel doesn't
+ *   map stuff there.  We do this by getting in first before
+ *   anything else is mapped (except this program) and reserve
+ *   the memory we want.
+ */
+void wld_reserve_wine_memory()
+{
+    mmap((void*)0x00400000, 0x01000000, PROT_NONE, 
+              MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
+}
+
+/*
+ *  wld_start
+ *
+ *  Repeat the actions the kernel would do when loading a dynamically linked .so
+ *   Load the ELF interperter ld-linux.so and then binary itself.
+ *
+ *  Note, we assume that the binary is a dynamically linked ELF shared object
+ *   and that ld-linux.so is its ELF interpreter.
+ */
+void* wld_start( int argc )
+{
+    int fd;
+    unsigned char buf[0x800], interp[0x100];
+    void *start;
+    char **argv = (char**) &argc, **p;
+    struct link_map map;
+    ElfW(auxv_t)* av;
+
+    memset( &map, 0, sizeof map );
+
+    /* skip over the parameters */
+    p = ++argv;
+#ifdef DUMP_ARG
+    {
+        int i;
+        for( i = 0; i<argc; i++ )
+            wld_printf("argv[%x] = %s\n", i, *p++);
+        if(*p++)
+            wld_printf("argv[argc] not NULL!!\n");
+    }
+#else
+    p+=(argc+1);
+#endif
+
+    /* skip over the environment */
+    while( *p++ )
+        ;
+
+    av = (ElfW(auxv_t)*) p;
+    wld_dump_auxilary( av );
+
+    /* reserve memory that Wine needs */
+    wld_reserve_wine_memory();
+
+    /* load the original dynamic loader itself */
+    fd = open( LDSO_NAME, O_RDONLY, 0);
+    if( fd < 0 )
+    {
+        wld_printf( "error: fd = %x\n", fd);
+        exit(1);
+    }
+    read( fd, buf, sizeof buf );
+    interp[0] = 0;
+    start = wld_map_so( fd, buf, sizeof buf, av, &map );
+    if( !start )
+        exit(0);
+    close( fd );
+
+    wld_set_auxilary( av, AT_BASE, map.l_addr );
+
+#ifdef DUMP_AUX_INFO
+    wld_printf("New auxillary info:\n");
+    wld_dump_auxilary( av );
+    wld_printf("jumping to %x\n", start);
+#endif
+
+    return start;
+}
+
+/*
+ * The _start function is the entry and exit point of this program
+ *
+ *  It calls wld_start, passing a pointer to the args it receives
+ *   then jumps to the address wld_start returns after removing the
+ *   first argv[] value, and decrementing argc
+ */
+void _start(void);
+__asm (
+	".align 4\n"
+	"\t.global _start\n"
+	"\t.type _start, at function\n"
+"_start:\n"
+	"\tcall wld_start\n"
+        "\tpush %eax\n"
+        "\txor %eax,%eax\n"
+        "\txor %ebx,%ebx\n"
+        "\txor %ecx,%ecx\n"
+        "\txor %edx,%edx\n"
+	"\tret\n"
+);
+
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ loader/preload.h	2004-04-10 21:43:23.000000000 +0900
@@ -0,0 +1,51 @@
+/* 
+   mini loader for ld.so  - x86 Linux 2.4.x+ kernels only
+ 
+   Copyright 2004 Mike McCormack for Codeweavers
+ 
+   Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA. 
+ */
+
+#define LDSO_NAME "/lib/ld-linux.so.2"
+
+/* ELF definitions */
+#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
+#define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
+
+#define __ELF_NATIVE_CLASS 32
+#define MAP_BASE_ADDR(l)     0
+#define MAP_COPY MAP_PRIVATE
+#define ANONFD -1
+
+#define ElfW(type)      _ElfW (Elf, __ELF_NATIVE_CLASS, type)
+#define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t)  e##w##t
+
+#include <stdio.h> 
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h> 
+#include <sys/types.h> 
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define GL(x) x
+
+extern unsigned int GL(dl_pagesize);
+


More information about the wine-devel mailing list