exec-shield workaround take 3

Mike McCormack mike at codeweavers.com
Sun Apr 11 03:16:42 CDT 2004


Hi,

Here's a new patch for exec-shield.

Changes against the last patch:

* reserve only the area required by the PE EXE if launched from
     CreateProcess (else just a default slab)
* pass the reserved area to wine, so wine knows about it
* unmap the preloader after it is no longer used

Again, comments, improvements, flames and test reports are all welcome!

Mike


-----------------------------------------------------------------------

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	11 Apr 2004 07:14:20 -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 char *unixpath )
 {
     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 += 6;
 
     if ((envp = malloc( count * sizeof(*envp) )))
     {
@@ -1093,6 +1094,11 @@
         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 );
+        if (unixpath) *envptr++ = alloc_env_string( "WINEPEEXEPATH=", unixpath );
+
         /* now put the Windows environment strings */
         for (p = env; *p; p += strlen(p) + 1)
         {
@@ -1131,7 +1137,7 @@
     if (!(pid = fork()))  /* child */
     {
         char **argv = build_argv( cmdline, 0 );
-        char **envp = build_envp( env, NULL );
+        char **envp = build_envp( env, NULL, NULL, NULL );
         close( fd[0] );
 
         /* Reset signals that we previously set to SIG_IGN */
@@ -1166,14 +1172,8 @@
     RTL_USER_PROCESS_PARAMETERS *params;
     UNICODE_STRING image_str, cmdline_str, desktop, title;
     NTSTATUS status;
-    WCHAR buffer[MAX_PATH];
-
-    if(!GetLongPathNameW( filename, buffer, MAX_PATH ))
-        lstrcpynW( buffer, filename, MAX_PATH );
-    if(!GetFullPathNameW( buffer, MAX_PATH, buffer, NULL ))
-        lstrcpynW( buffer, filename, MAX_PATH );
-    RtlInitUnicodeString( &image_str, buffer );
 
+    RtlInitUnicodeString( &image_str, filename );
     RtlInitUnicodeString( &cmdline_str, cmdline );
     if (startup->lpDesktop) RtlInitUnicodeString( &desktop, startup->lpDesktop );
     if (startup->lpTitle) RtlInitUnicodeString( &title, startup->lpTitle );
@@ -1225,6 +1225,7 @@
     pid_t pid;
     int err;
     char dummy = 0;
+    WCHAR buffer[MAX_PATH];
 
     if (!env)
     {
@@ -1232,7 +1233,12 @@
         extra_env = DRIVE_BuildEnv();
     }
 
-    if (!(params = create_user_params( filename, cmd_line, startup )))
+    if(!GetLongPathNameW( filename, buffer, MAX_PATH ))
+        lstrcpynW( buffer, filename, MAX_PATH );
+    if(!GetFullPathNameW( buffer, MAX_PATH, buffer, NULL ))
+        lstrcpynW( buffer, filename, MAX_PATH );
+
+    if (!(params = create_user_params( buffer, cmd_line, startup )))
     {
         if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
         return FALSE;
@@ -1263,7 +1269,20 @@
     if (!(pid = fork()))  /* child */
     {
         char **argv = build_argv( cmd_line, 1 );
-        char **envp = build_envp( env, extra_env );
+        char **envp;
+        CHAR unixpath[MAX_PATH];
+
+        unixpath[0]=0;
+        wine_get_unix_file_name( buffer, unixpath, MAX_PATH );
+
+        /* 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, unixpath );
 
         close( startfd[1] );
         close( execfd[0] );
@@ -1275,8 +1294,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	11 Apr 2004 07:14:21 -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	11 Apr 2004 07:14:21 -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 main.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	11 Apr 2004 07:14:21 -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	11 Apr 2004 07:14:21 -0000
@@ -20,19 +20,44 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
 #include "wine/library.h"
 
+#include "main.h"
+
+/* this variable is set by the wine-specific shared object loader */
+struct wine_preload_info *wine_preload_vars;
+
 /**********************************************************************
  *           main
  */
 int main( int argc, char *argv[] )
 {
-    char error[1024];
+    char error[1024], *dir;
+
+    if( wine_preload_vars )
+    {
+        /* printf("vars(*%p) = %p -> (%p, %08x) (%p, %08x)\n",
+               &wine_preload_vars, wine_preload_vars,
+                wine_preload_vars->pe_address,
+                wine_preload_vars->pe_size,
+                wine_preload_vars->preload_base,
+                wine_preload_vars->preload_size ); */
+
+        /* let wine know which area was mapped */
+        wine_set_pe_load_area(wine_preload_vars->pe_address,
+                              wine_preload_vars->pe_size);
+
+        /* unmap the preloader itself */
+        munmap( wine_preload_vars->preload_base, 
+                wine_preload_vars->preload_size );
+        wine_preload_vars = NULL;
+    }
 
-#if 0
-    static char pe_load[256*1024*1024] __attribute__((aligned(4096)));
-    wine_set_pe_load_area( pe_load, sizeof(pe_load) );
-#endif
+    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/main.h	2004-04-11 16:58:25.000000000 +0900
@@ -0,0 +1,8 @@
+
+struct wine_preload_info
+{
+    void    *pe_address;
+    size_t   pe_size;
+    void    *preload_base;
+    size_t   preload_size;
+};
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ loader/preload.h	2004-04-11 16:58:25.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);
+
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ loader/preload.c	2004-04-11 16:58:25.000000000 +0900
@@ -0,0 +1,727 @@
+/* 
+   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
+ *
+ * This program will be invoked by linking it as the shared object loader
+ *  using the link time flags --shared-loader=ld-winepreload.so . Since we
+ *  are not using an absolute path, an executable using this shared object
+ *  loader must be invoked from the directory that contains this loader.
+ *
+ * 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
+ *
+ * If this program is used as the shared object loader. the only difference
+ *  that the loaded programs should see is that this loader will be mapped
+ *  into memory when it starts.
+ *
+ *  Limitations:
+ *   * using a non-absolution path for the shared object loader is not standard
+ */
+
+/*
+ * 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 "main.h"
+#include <elf.h>
+
+/* the following headers are needed to get the definition for IMAGE_NT_HEADERS */
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+
+/* debugging */
+#undef DUMP_SEGMENTS
+#undef DUMP_ARG
+#undef DUMP_AUX_INFO
+#undef DUMP_SYMS
+
+unsigned int GL(dl_pagesize) = 0x0000;
+extern const unsigned char _end[];
+
+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;
+}
+
+#ifdef DUMP_AUX_INFO
+/*
+ *  Dump interesting bits of the ELF auxv_t structure that is passed
+ *   as the 4th parameter to the _start function
+ */
+static void wld_dump_auxilary( ElfW(auxv_t) *av )
+{
+  for (  ; av->a_type != AT_NULL; av++)
+    switch (av->a_type)
+      {
+      case AT_PAGESZ:
+        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_get_auxilary
+ *
+ * Get a field of the auxillary structure
+ */
+int wld_get_auxilary( ElfW(auxv_t) *av, int type, int *val )
+{
+  for ( ; av->a_type != AT_NULL; av++)
+    if( av->a_type == type )
+    {
+      *val = av->a_un.a_val;
+      return 1;
+    }
+  return 0;
+}
+
+/*
+ * 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, 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;
+}
+
+#define ROUND_SIZE(addr,size) \
+   (((int)(size) + ((int)(addr) & page_mask) + page_mask) & ~page_mask)
+
+/*
+ *  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( char *pe_file, void **base, size_t *size )
+{
+    IMAGE_DOS_HEADER dos;
+    IMAGE_NT_HEADERS nt;
+    int unix_fd, page_mask = GL(dl_pagesize) - 1;
+
+    if( !pe_file )
+        return;
+
+    unix_fd = open( pe_file, O_RDONLY, 0);
+    if( unix_fd < 0 )
+        return;
+
+    /*
+     * The following code is borrowed from 
+     * get_image_params() in wine/server/mapping.c
+     */
+    if (read( unix_fd, &dos, sizeof(dos) ) != sizeof(dos)) goto error;
+    if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto error;
+    if (lseek( unix_fd, dos.e_lfanew, SEEK_SET ) == -1) goto error;
+
+    if (read( unix_fd, &nt.Signature, sizeof(nt.Signature) ) != sizeof(nt.Signature)) goto error;
+    if (nt.Signature != IMAGE_NT_SIGNATURE) goto error;
+    if (read( unix_fd, &nt.FileHeader, sizeof(nt.FileHeader) ) != sizeof(nt.FileHeader)) goto error;
+    /* zero out Optional header in the case it's not present or partial */
+    memset(&nt.OptionalHeader, 0, sizeof(nt.OptionalHeader));
+    if (read( unix_fd, &nt.OptionalHeader, nt.FileHeader.SizeOfOptionalHeader) != nt.FileHeader.SizeOfOptionalHeader) goto error;
+
+    *size = ROUND_SIZE( 0, nt.OptionalHeader.SizeOfImage );
+    *base = (void *)nt.OptionalHeader.ImageBase;
+
+#ifdef DUMP_PERESERVE
+    wld_printf( "parsed PE image... base %x size %x\n", *base, *size );
+#endif
+
+error:
+    close( unix_fd );
+}
+
+/*
+ * Find a symbol in the symbol table of the executable loaded
+ */
+void *wld_find_symbol( const ElfW(Phdr) *phdr, int num, char *var )
+{
+    const ElfW(Dyn) *dyn = NULL;
+    void *ptr = NULL;
+    const ElfW(Phdr) *ph;
+    const ElfW(Sym) *symtab = NULL;
+    const char *strings = NULL;
+ 
+    /* check the values */
+#ifdef DUMP_SYMS
+    wld_printf("%x %x\n", phdr, num );
+#endif
+    if( ( phdr == NULL ) || ( num == 0 ) )
+    {
+        wld_printf("could not find PT_DYNAMIC header entry\n");
+        return NULL;
+    }
+
+    /* parse the (already loaded) ELF executable's header */
+    for (ph = phdr; ph < &phdr[num]; ++ph)
+    {
+        if( PT_DYNAMIC == ph->p_type )
+        {
+            dyn = (void *) ph->p_vaddr;
+            num = ph->p_memsz / sizeof (Elf32_Dyn);
+            break;
+        }
+    }
+    if( !dyn )
+    {
+        wld_printf("No PT_DYNAMIC section\n");
+        return ptr;
+    }
+
+    while( dyn->d_tag )
+    {
+        if( dyn->d_tag == DT_STRTAB )
+            strings = (const char*) dyn->d_un.d_ptr;
+        if( dyn->d_tag == DT_SYMTAB )
+            symtab = (const ElfW(Sym) *)dyn->d_un.d_ptr;
+#ifdef DUMP_SYMS
+        wld_printf("%x %x\n", dyn->d_tag, dyn->d_un.d_ptr );
+#endif
+        dyn++;
+    }
+
+    if( (!symtab) || (!strings) )
+    {
+        wld_printf("No symbol table found\n");
+        return ptr;
+    }
+
+    /* FIXME: this crashes if it doesn't find the symbol we want */
+    do
+    {
+        if( ( ELF32_ST_BIND(symtab->st_info) == STT_OBJECT ) &&
+            ( 0 == strcmp( strings+symtab->st_name, var ) ) )
+        {
+            ptr = (void*)symtab->st_value;
+            break;
+        }
+        symtab++;
+    }
+    while( symtab->st_name );
+
+#ifdef DUMP_SYMS
+    wld_printf("Found %s -> %x\n", strings+symtab->st_name, symtab->st_value );
+#endif
+
+    return ptr;
+}
+
+/*
+ *  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, phnum = 0;
+    unsigned char buf[0x800];
+    void *start;
+    char **argv = (char**) &argc, **p, *pe_file = NULL;
+    struct link_map map;
+    ElfW(auxv_t)* av;
+    ElfW(Phdr) *elf_phdr = NULL;
+    struct wine_preload_info **wine_preload_vars;
+    static struct wine_preload_info vars;
+
+    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, find the name of the PE file */
+    while( *p )
+    {
+        const char pefile[] = "WINEPEEXEPATH=";
+        if( 0 == memcmp( pefile, *p, sizeof pefile - 1 ) )
+            pe_file = (*p) + sizeof pefile - 1 ;
+        p++;
+    }
+
+    av = (ElfW(auxv_t)*) (p+1);
+    wld_get_auxilary( av, AT_PAGESZ, &dl_pagesize );
+#ifdef DUMP_AUX_INFO
+    wld_dump_auxilary( av );
+#endif
+
+    /* reserve memory that Wine needs */
+    vars.pe_size = 0x01000000;
+    vars.pe_address = (void*)0x00400000;
+    wld_reserve_wine_memory( pe_file, &vars.pe_address, &vars.pe_size );
+    mmap( vars.pe_address, vars.pe_size, PROT_NONE, 
+          MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
+
+    /* 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 );
+    start = wld_map_so( fd, buf, sizeof buf, &map );
+    if( !start )
+        exit(0);
+    close( fd );
+
+    /*
+     * Pass information to the wine loader in a static variable
+     * so it doesn't get destroyed after we leave this stack frame
+     */
+    wld_get_auxilary( av, AT_PHDR, (int*)&elf_phdr );
+    wld_get_auxilary( av, AT_PHNUM, (int*)&phnum );
+    wine_preload_vars = wld_find_symbol( elf_phdr, phnum, "wine_preload_vars" );
+    if( wine_preload_vars )
+    {
+        unsigned char *base = (void*)0x7ff81000; /* FIXME - define properly */
+        vars.preload_base = (void*) base;
+        vars.preload_size = (size_t) (_end - base);
+        *wine_preload_vars = &vars;
+    }
+
+    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"
+);
+


More information about the wine-devel mailing list