Description:
This patch allows platforms using exec-shield patch against the Linux kernel (eg. Fedora Core) to run Wine without disabling exec-shield.
License: LGPL
Changes in version 7: * fix bug that caused crash when CreateProcess was called * update to work with the new functions for reserving memory
Changes in version 6: * include ulimit workaround * resolve conflict with recent patch to WineHQ CVS tip
Changes in version 5: * reserve the shared heap area as well as the PE load area * allow multiple reserve areas in libwine
Changes in version 4: * set the WINEPEEXEPATH environment variable correctly
Changes in version 3: * 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
Changes in verison 2: * replace the shared object loader instead of using a bootstrap exe
Version 1: * create a bootstrap exe that reserves memory before loading wine-pthread or wine-kthread
Index: dlls/kernel/process.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/process.c,v retrieving revision 1.63 diff -u -r1.63 process.c --- dlls/kernel/process.c 25 May 2004 01:29:24 -0000 1.63 +++ dlls/kernel/process.c 25 May 2004 04:59:54 -0000 @@ -1257,7 +1257,8 @@ * * Build the environment of a new child process. */ -static char **build_envp( const WCHAR *envW ) +static char **build_envp( const WCHAR *envW, + const char *workdir, const char *unixpath ) { const WCHAR *p; char **envp; @@ -1270,7 +1271,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) ))) { @@ -1283,12 +1284,19 @@ 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 (workdir) *envptr++ = alloc_env_string( "WINEPEEXEPATH=", unixpath ); + /* now put the Windows environment strings */ for (p = env; *p; p += strlen(p) + 1) { if (is_special_env_var( p )) /* prefix it with "WINE" */ *envptr++ = alloc_env_string( "WINE", p ); else if (strncmp( p, "HOME=", 5 ) && + strncmp( p, "WINEWORKDIR=", 12 ) && + strncmp( p, "WINEPEEXEPATH=", 14 ) && strncmp( p, "WINEPREFIX=", 11 )) *envptr++ = p; } *envptr = 0; @@ -1319,7 +1327,7 @@ if (!(pid = fork())) /* child */ { char **argv = build_argv( cmdline, 0 ); - char **envp = build_envp( env ); + char **envp = build_envp( env, NULL, NULL ); close( fd[0] );
/* Reset signals that we previously set to SIG_IGN */ @@ -1354,14 +1362,8 @@ RTL_USER_PROCESS_PARAMETERS *params; UNICODE_STRING image_str, cmdline_str, curdir_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 (cur_dir) RtlInitUnicodeString( &curdir_str, cur_dir ); if (startup->lpDesktop) RtlInitUnicodeString( &desktop, startup->lpDesktop ); @@ -1415,10 +1417,16 @@ pid_t pid; int err; char dummy = 0; + WCHAR buffer[MAX_PATH];
if (!env) env = GetEnvironmentStringsW();
- if (!(params = create_user_params( filename, cmd_line, cur_dir, 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, cur_dir, startup ))) return FALSE;
/* create the synchronization pipes */ @@ -1444,7 +1452,20 @@ if (!(pid = fork())) /* child */ { char **argv = build_argv( cmd_line, 1 ); - char **envp = build_envp( env ); + char **envp = build_envp( env, NULL, NULL ); + char *unixpath; + + unixpath = wine_get_unix_file_name( buffer ); + + /* 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, unixdir, unixpath ); + HeapFree( GetProcessHeap(), 0, unixpath );
close( startfd[1] ); close( execfd[0] ); @@ -1456,8 +1477,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.4 diff -u -r1.4 config.c --- libs/wine/config.c 11 May 2004 04:29:18 -0000 1.4 +++ libs/wine/config.c 25 May 2004 04:59:54 -0000 @@ -271,6 +271,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 ) @@ -285,7 +302,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 */ @@ -294,7 +311,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] ); }
@@ -311,7 +328,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 25 May 2004 04:59:54 -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 25 May 2004 04:59:54 -0000 @@ -29,6 +29,7 @@ #ifdef HAVE_PTHREAD_H # include <pthread.h> #endif +#include <sys/resource.h>
#include "wine/library.h"
@@ -75,6 +76,23 @@ { const char *loader = getenv( "WINELOADER" ); const char *threads = get_threading(); + char cwd[MAX_PATH], *cwd_var; + + /* hack to work-around broken address space layout in RHEL3 */ + struct rlimit limit; + getrlimit( RLIMIT_STACK, &limit ); + limit.rlim_cur = limit.rlim_max; + setrlimit( RLIMIT_STACK, &limit ); + + 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 +104,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.91 diff -u -r1.91 main.c --- loader/main.c 25 May 2004 01:29:24 -0000 1.91 +++ loader/main.c 25 May 2004 04:59:54 -0000 @@ -20,14 +20,52 @@
#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 ) + { +#if 0 + printf("vars(*%p) = %p -> (%p, %08x) (%p, %08x) (%p, %08x)\n", + &wine_preload_vars, wine_preload_vars, + wine_preload_vars->pe_address, + wine_preload_vars->pe_size, + wine_preload_vars->heap_address, + wine_preload_vars->heap_size, + wine_preload_vars->preload_base, + wine_preload_vars->preload_size ); +#endif + + /* let wine know which area was mapped for the PE EXE */ + wine_mmap_add_reserved_area( wine_preload_vars->pe_address, + wine_preload_vars->pe_size); + + /* let wine know which area was mapped for the system heap */ + wine_mmap_add_reserved_area( wine_preload_vars->heap_address, + wine_preload_vars->heap_size); + + /* unmap the preloader itself */ + munmap( wine_preload_vars->preload_base, + wine_preload_vars->preload_size ); + wine_preload_vars = NULL; + } + + 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-15 17:29:17.000000000 +0900 @@ -0,0 +1,10 @@ + +struct wine_preload_info +{ + void *pe_address; + size_t pe_size; + void *heap_address; + size_t heap_size; + void *preload_base; + size_t preload_size; +}; --- /dev/null 1994-07-18 08:46:18.000000000 +0900 +++ loader/preload.h 2004-04-15 17:29:17.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-05-25 15:04:52.000000000 +0900 @@ -0,0 +1,750 @@ +/* + 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@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.htm... + */ + +#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 +#undef DUMP_PERESERVE + +/* older systems may not define these */ +#ifndef PT_TLS +#define PT_TLS 7 +#endif +#ifndef PT_GNU_EH_FRAME +#define PT_GNU_EH_FRAME 0x6474e550 +#endif +#ifndef PT_GNU_STACK +#define PT_GNU_STACK 0x6474e551 +#endif +#ifndef PT_GNU_RELRO +#define PT_GNU_RELRO 0x6474e552 +#endif + +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[1] != 'E' ) || + ( header->e_ident[2] != 'L' ) || + ( header->e_ident[3] != '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 PT_GNU_STACK: + case PT_GNU_RELRO: + 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( "file %s\n", pe_file ); + 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); + + /* reserve the memory that wine needs for the global heap */ + vars.heap_size = 0x400000; + vars.heap_address = (void*)0x65430000; + mmap( vars.heap_address, vars.heap_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,@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" +); +