Commit b1abca8a authored by Alexandre Julliard's avatar Alexandre Julliard

Initial version of the Wine preloader, used to reserve memory

areas at startup. Based on the work of Mike McCormack.
parent ed8a41c7
......@@ -14180,31 +14180,44 @@ echo "${ECHO_T}$ac_cv_c_export_dynamic" >&6
case $host_cpu in
*i[3456789]86*)
echo "$as_me:$LINENO: checking whether we can relocate the executable to 0x00110000" >&5
echo $ECHO_N "checking whether we can relocate the executable to 0x00110000... $ECHO_C" >&6
echo "$as_me:$LINENO: checking whether we can relocate the executable to 0x77f00000" >&5
echo $ECHO_N "checking whether we can relocate the executable to 0x77f00000... $ECHO_C" >&6
if test "${ac_cv_ld_reloc_exec+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wl,--section-start,.interp=0x00110400"
if test "$cross_compiling" = yes; then
ac_cv_ld_reloc_exec="no"
else
cat >conftest.$ac_ext <<_ACEOF
ac_wine_try_cflags_saved=$CFLAGS
CFLAGS="$CFLAGS -Wl,--section-start,.interp=0x77f00400"
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <unistd.h>
int main() { return (sbrk(32*1024*1024) == (void *)-1); }
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest$ac_exeext
rm -f conftest.$ac_objext conftest$ac_exeext
if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(eval $ac_link) 2>&5
(eval $ac_link) 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && { ac_try='./conftest$ac_exeext'
(exit $ac_status); } &&
{ ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; } &&
{ ac_try='test -s conftest$ac_exeext'
{ (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
(eval $ac_try) 2>&5
ac_status=$?
......@@ -14212,22 +14225,20 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
(exit $ac_status); }; }; then
ac_cv_ld_reloc_exec="yes"
else
echo "$as_me: program exited with status $ac_status" >&5
echo "$as_me: failed program was:" >&5
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
( exit $ac_status )
ac_cv_ld_reloc_exec="no"
fi
rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
fi
CFLAGS="$saved_CFLAGS"
rm -f conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
CFLAGS=$ac_wine_try_cflags_saved
fi
echo "$as_me:$LINENO: result: $ac_cv_ld_reloc_exec" >&5
echo "${ECHO_T}$ac_cv_ld_reloc_exec" >&6
if test "$ac_cv_ld_reloc_exec" = "yes"
then
LDEXECFLAGS="$LDEXECFLAGS -Wl,--section-start,.interp=0x00110400"
LDEXECFLAGS="$LDEXECFLAGS -Wl,--section-start,.interp=0x77f00400"
fi
;;
esac
......@@ -14528,7 +14539,7 @@ esac
case $host_os in
linux*)
WINE_BINARIES="wine-glibc wine-kthread wine-pthread"
WINE_BINARIES="wine-glibc wine-kthread wine-pthread wine-preloader"
MAIN_BINARY="wine-glibc"
......
......@@ -905,16 +905,12 @@ case $host_os in
case $host_cpu in
*i[[3456789]]86*)
AC_CACHE_CHECK([whether we can relocate the executable to 0x00110000], ac_cv_ld_reloc_exec,
[saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wl,--section-start,.interp=0x00110400"
AC_TRY_RUN([#include <unistd.h>
int main() { return (sbrk(32*1024*1024) == (void *)-1); }],
ac_cv_ld_reloc_exec="yes", ac_cv_ld_reloc_exec="no", ac_cv_ld_reloc_exec="no")
CFLAGS="$saved_CFLAGS"])
AC_CACHE_CHECK([whether we can relocate the executable to 0x77f00000], ac_cv_ld_reloc_exec,
[WINE_TRY_CFLAGS([-Wl,--section-start,.interp=0x77f00400],
ac_cv_ld_reloc_exec="yes", ac_cv_ld_reloc_exec="no")])
if test "$ac_cv_ld_reloc_exec" = "yes"
then
LDEXECFLAGS="$LDEXECFLAGS -Wl,--section-start,.interp=0x00110400"
LDEXECFLAGS="$LDEXECFLAGS -Wl,--section-start,.interp=0x77f00400"
fi
;;
esac
......@@ -980,7 +976,7 @@ esac
case $host_os in
linux*)
AC_SUBST(WINE_BINARIES,"wine-glibc wine-kthread wine-pthread")
AC_SUBST(WINE_BINARIES,"wine-glibc wine-kthread wine-pthread wine-preloader")
AC_SUBST(MAIN_BINARY,"wine-glibc")
;;
*)
......
......@@ -1462,10 +1462,10 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
if (argv && envp)
{
/* first, try for a WINELOADER environment variable */
argv[0] = getenv("WINELOADER");
if (argv[0]) execve( argv[0], argv, envp );
const char *loader = getenv("WINELOADER");
if (loader) wine_exec_wine_binary( loader, argv, envp, TRUE );
/* now use the standard search strategy */
wine_exec_wine_binary( NULL, argv, envp );
wine_exec_wine_binary( NULL, argv, envp, TRUE );
}
err = errno;
write( execfd[1], &err, sizeof(err) );
......
......@@ -512,7 +512,7 @@ static void start_server( const char *oldcwd )
" specified in the WINESERVER environment variable", p );
}
/* now use the standard search strategy */
wine_exec_wine_binary( argv[0], argv, NULL );
wine_exec_wine_binary( argv[0], argv, NULL, FALSE );
fatal_error( "could not exec wineserver\n" );
}
waitpid( pid, &status, 0 );
......@@ -703,7 +703,7 @@ static void create_config_dir(void)
argv[2] = "--prefix";
argv[3] = tmp_dir;
argv[4] = NULL;
wine_exec_wine_binary( argv[0], (char **)argv, NULL );
wine_exec_wine_binary( argv[0], (char **)argv, NULL, FALSE );
rmdir( tmp_dir );
fatal_perror( "could not exec wineprefixcreate" );
}
......
......@@ -33,7 +33,7 @@ extern const char *wine_get_config_dir(void);
extern const char *wine_get_server_dir(void);
extern const char *wine_get_user_name(void);
extern void wine_init_argv0_path( const char *argv0 );
extern void wine_exec_wine_binary( const char *name, char **argv, char **envp );
extern void wine_exec_wine_binary( const char *name, char **argv, char **envp, int use_preloader );
/* dll loading */
......
......@@ -271,20 +271,50 @@ const char *wine_get_user_name(void)
return user_name;
}
/* exec a binary using the preloader if requested; helper for wine_exec_wine_binary */
static void preloader_exec( char **argv, char **envp, int use_preloader )
{
#ifdef linux
if (use_preloader)
{
static const char preloader[] = "wine-preloader";
char *p, *full_name;
if (!(p = strrchr( argv[0], '/' ))) p = argv[0];
else p++;
full_name = xmalloc( p - argv[0] + sizeof(preloader) );
memcpy( full_name, argv[0], p - argv[0] );
memcpy( full_name + (p - argv[0]), preloader, sizeof(preloader) );
if (envp) execve( full_name, argv, envp );
else execv( full_name, argv );
free( full_name );
return;
}
#else
if (envp) execve( argv[0], argv, envp );
else execv( argv[0], argv );
#endif
}
/* 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 )
void wine_exec_wine_binary( const char *name, char **argv, char **envp, int use_preloader )
{
const char *path, *pos, *ptr;
if (!envp) envp = environ;
if (!name) name = argv0_name;
if (name && strchr( name, '/' ))
{
argv[0] = (char *)name;
preloader_exec( argv, envp, use_preloader );
return;
}
else if (!name) name = argv0_name;
/* first, try bin directory */
argv[0] = xmalloc( sizeof(BINDIR "/") + strlen(name) );
strcpy( argv[0], BINDIR "/" );
strcat( argv[0], name );
execve( argv[0], argv, envp );
preloader_exec( argv, envp, use_preloader );
free( argv[0] );
/* now try the path of argv0 of the current binary */
......@@ -293,7 +323,7 @@ void wine_exec_wine_binary( const char *name, char **argv, char **envp )
argv[0] = xmalloc( strlen(argv0_path) + strlen(name) + 1 );
strcpy( argv[0], argv0_path );
strcat( argv[0], name );
execve( argv[0], argv, envp );
preloader_exec( argv, envp, use_preloader );
free( argv[0] );
}
......@@ -310,7 +340,7 @@ void wine_exec_wine_binary( const char *name, char **argv, char **envp )
memcpy( argv[0], pos, ptr - pos );
strcpy( argv[0] + (ptr - pos), "/" );
strcat( argv[0] + (ptr - pos), name );
execve( argv[0], argv, envp );
preloader_exec( argv, envp, use_preloader );
pos = ptr;
}
free( argv[0] );
......
......@@ -2,4 +2,5 @@ Makefile
wine
wine-glibc
wine-kthread
wine-preloader
wine-pthread
......@@ -8,6 +8,7 @@ C_SRCS = \
glibc.c \
kthread.c \
main.c \
preloader.c \
pthread.c
KTHREAD_OBJS = kthread.o main.o
......@@ -24,7 +25,10 @@ LIBPTHREAD = @LIBPTHREAD@
LDEXECFLAGS = @LDEXECFLAGS@
wine-glibc: glibc.o Makefile.in
$(CC) -o $@ $(LDEXECFLAGS) glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
$(CC) -o $@ glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
wine-preloader: preloader.o Makefile.in
$(CC) -o $@ -static -nostartfiles -Wl,-Ttext=0x78000000 preloader.o $(LIBPORT) $(LDFLAGS)
wine-kthread: $(KTHREAD_OBJS) Makefile.in
$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS)
......
......@@ -93,13 +93,12 @@ int main( int argc, char *argv[] )
strcpy( new_loader, "WINELOADER=" );
strcat( new_loader, new_name );
putenv( new_loader );
argv[0] = new_name;
execv( argv[0], argv );
wine_exec_wine_binary( new_name, argv, NULL, TRUE );
}
else
{
wine_init_argv0_path( argv[0] );
wine_exec_wine_binary( threads, argv, NULL );
wine_exec_wine_binary( threads, argv, NULL, TRUE );
}
fprintf( stderr, "wine: could not exec %s\n", argv[0] );
exit(1);
......
......@@ -21,6 +21,10 @@
#include <stdio.h>
#include <stdlib.h>
#include "wine/library.h"
#include "main.h"
/* the preloader will set this variable */
const struct wine_preload_info *wine_main_preload_info = NULL;
/**********************************************************************
* main
......@@ -28,6 +32,14 @@
int main( int argc, char *argv[] )
{
char error[1024];
int i;
if (wine_main_preload_info)
{
for (i = 0; wine_main_preload_info[i].size; i++)
wine_mmap_add_reserved_area( wine_main_preload_info[i].addr,
wine_main_preload_info[i].size );
}
wine_init( argc, argv, error, sizeof(error) );
fprintf( stderr, "wine: failed to initialize: %s\n", error );
......
/*
* Definitions for Wine main program
*
* Copyright 2004 Mike McCormack for Codeweavers
* Copyright 2004 Alexandre Julliard
*
* This 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.
*
* This 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __WINE_LOADER_MAIN_H
#define __WINE_LOADER_MAIN_H
struct wine_preload_info
{
void *addr;
size_t size;
};
#endif /* __WINE_LOADER_MAIN_H */
/*
* Preloader for ld.so
*
* Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
* Copyright (C) 2004 Mike McCormack for Codeweavers
* Copyright (C) 2004 Alexandre Julliard
*
* This 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.
*
* This 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 this 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 main binary and its ELF
* interpreter.
*
* We will try to set up the stack and 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 - 0x00110000 the DOS area
* 0x80000000 - 0x81000000 the shared heap
* ??? - ??? the PE binary load address (usually starting at 0x00400000)
*
* 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.
*/
/*
* 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.html
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_ELF_H
# include <elf.h>
#endif
#ifdef HAVE_LINK_H
# include <link.h>
#endif
#ifdef HAVE_SYS_LINK_H
# include <sys/link.h>
#endif
#include "main.h"
/* ELF definitions */
#define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
#define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
#define MAP_BASE_ADDR(l) 0
#ifndef MAP_COPY
#define MAP_COPY MAP_PRIVATE
#endif
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif
static struct wine_preload_info preload_info[] =
{
{ (void *)0x00000000, 0x00110000 }, /* DOS area */
{ (void *)0x80000000, 0x01000000 }, /* shared heap */
{ (void *)0x00110000, 0x0fef0000 }, /* PE exe range (FIXME) */
{ 0, 0 } /* end of list */
};
/* debugging */
#undef DUMP_SEGMENTS
#undef DUMP_AUX_INFO
#undef DUMP_SYMS
/* older systems may not define these */
#ifndef PT_TLS
#define PT_TLS 7
#endif
static unsigned int page_size, page_mask;
struct wld_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;
ElfW(Addr) l_interp;
};
/*
* 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();
extern char _end[];
__ASM_GLOBAL_FUNC(_start,
"\tcall wld_start\n"
"\tpush %eax\n"
"\txor %eax,%eax\n"
"\txor %ecx,%ecx\n"
"\txor %edx,%edx\n"
"\tret\n")
/*
* wld_printf - just the basics
*
* %x prints a hex number
* %s prints a string
*/
static void wld_vsprintf(char *str, char *fmt, va_list args )
{
char *p = fmt;
while( *p )
{
if( *p == '%' )
{
p++;
if( *p == 'x' )
{
int ch, i, x = va_arg( args, int );
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 = va_arg( args, char * );
while(*s)
*str++ = *s++;
}
else if( *p == 0 )
break;
p++;
}
*str++ = *p++;
}
*str = 0;
}
static void wld_printf(char *fmt, ... )
{
va_list args;
char buffer[256];
va_start( args, fmt );
wld_vsprintf(buffer, fmt, args );
va_end( args );
write(2, buffer, strlen(buffer));
}
static void fatal_error(char *fmt, ... )
{
va_list args;
char buffer[256];
va_start( args, fmt );
wld_vsprintf(buffer, fmt, args );
va_end( args );
write(2, buffer, strlen(buffer));
_exit(1);
}
#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 dump_auxiliary( 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
/*
* set_auxiliary
*
* Set a field of the auxiliary structure
*/
static void set_auxiliary( 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;
}
/*
* get_auxiliary
*
* Get a field of the auxiliary structure
*/
static int get_auxiliary( 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;
}
/*
* map_so_lib
*
* 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.
*/
static void map_so_lib( const char *name, struct wld_link_map *l)
{
int fd;
unsigned char buf[0x800];
ElfW(Ehdr) *header = (ElfW(Ehdr)*)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[16], *c;
size_t nloadcmds = 0, maplength;
fd = open( name, O_RDONLY );
if (fd == -1) fatal_error("%s: could not open\n", name );
if (read( fd, buf, sizeof(buf) ) != sizeof(buf))
fatal_error("%s: failed to read ELF header\n", name);
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' ) )
fatal_error( "%s: not an ELF binary... don't know how to load it\n", name );
if( header->e_machine != EM_386 )
fatal_error("%s: not an i386 ELF binary... don't know how to load it\n", name );
if (header->e_phnum > sizeof(loadcmds)/sizeof(loadcmds[0]))
fatal_error( "%s: oops... not enough space for load commands\n", name );
maplength = header->e_phnum * sizeof (ElfW(Phdr));
if (header->e_phoff + maplength > sizeof(buf))
fatal_error( "%s: oops... not enough space for ELF headers\n", name );
l->l_ld = 0;
l->l_addr = 0;
l->l_phdr = 0;
l->l_phnum = header->e_phnum;
l->l_entry = header->e_entry;
l->l_interp = 0;
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_LOAD:
{
if ((ph->p_align & page_mask) != 0)
fatal_error( "%s: ELF load command alignment not page-aligned\n", name );
if (((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1)) != 0)
fatal_error( "%s: ELF load command address/offset not properly aligned\n", name );
c = &loadcmds[nloadcmds++];
c->mapstart = ph->p_vaddr & ~(ph->p_align - 1);
c->mapend = ((ph->p_vaddr + ph->p_filesz + page_mask) & ~page_mask);
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:
l->l_interp = 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.
*/
case PT_SHLIB:
case PT_NOTE:
default:
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
{
char *preloader_start = (char *)_start - ((unsigned int)_start & page_mask);
char *preloader_end = (char *)((unsigned int)(_end + page_mask) & ~page_mask);
/* sanity check */
if ((char *)c->mapstart + maplength > preloader_start &&
(char *)c->mapstart <= preloader_end)
fatal_error( "%s: binary overlaps preloader (%x-%x)\n",
name, c->mapstart, (char *)c->mapstart + maplength );
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 *)(unsigned int) (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 + page_mask) & ~page_mask;
/*
* This is different from the dl-load load...
* ld-linux.so.2 relies on the whole page being zero'ed
*/
zeroend = (zeroend + page_mask) & ~page_mask;
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 & ~page_mask), page_size, c->prot|PROT_WRITE);
}
memset ((void *) zero, '\0', zeropage - zero);
if ((c->prot & PROT_WRITE) == 0)
mprotect ((caddr_t) (zero & ~page_mask), page_size, 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,
-1, 0);
}
}
++c;
}
if (l->l_phdr == NULL) fatal_error("no program header\n");
l->l_phdr = (void *)((ElfW(Addr))l->l_phdr + l->l_addr);
l->l_entry += l->l_addr;
close( fd );
}
/*
* Find a symbol in the symbol table of the executable loaded
*/
static void *find_symbol( const ElfW(Phdr) *phdr, int num, char *var )
{
const ElfW(Dyn) *dyn = NULL;
const ElfW(Phdr) *ph;
const ElfW(Sym) *symtab = NULL;
const char *strings = NULL;
Elf_Symndx i, symtabend = 0;
/* 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 ) return NULL;
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;
if( dyn->d_tag == DT_HASH )
symtabend = *((const Elf_Symndx *)dyn->d_un.d_ptr + 1);
#ifdef DUMP_SYMS
wld_printf("%x %x\n", dyn->d_tag, dyn->d_un.d_ptr );
#endif
dyn++;
}
if( (!symtab) || (!strings) ) return NULL;
for (i = 0; i < symtabend; i++)
{
if( ( ELF32_ST_BIND(symtab[i].st_info) == STT_OBJECT ) &&
( 0 == strcmp( strings+symtab[i].st_name, var ) ) )
{
#ifdef DUMP_SYMS
wld_printf("Found %s -> %x\n", strings+symtab[i].st_name, symtab[i].st_value );
#endif
return (void*)symtab[i].st_value;
}
}
return NULL;
}
/*
* wld_start
*
* Repeat the actions the kernel would do when loading a dynamically linked .so
* Load the binary and then its ELF interpreter.
* Note, we assume that the binary is a dynamically linked ELF shared object.
*/
void* wld_start( int argc, ... )
{
int i;
char **argv, **p;
char *interp;
ElfW(auxv_t)* av;
struct wld_link_map main_binary_map, ld_so_map;
struct wine_preload_info **wine_main_preload_info;
argv = (char **)&argc + 1;
/* skip over the parameters */
p = argv + argc + 1;
/* skip over the environment */
while (*p) p++;
av = (ElfW(auxv_t)*) (p+1);
if (!get_auxiliary( av, AT_PAGESZ, &page_size )) page_size = 4096;
page_mask = page_size - 1;
#ifdef DUMP_AUX_INFO
for( i = 0; i<argc; i++ ) wld_printf("argv[%x] = %s\n", i, argv[i]);
dump_auxiliary( av );
#endif
/* reserve memory that Wine needs */
for (i = 0; preload_info[i].size; i++)
mmap( preload_info[i].addr, preload_info[i].size,
PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0 );
/* load the main binary */
map_so_lib( argv[0], &main_binary_map );
/* load the ELF interpreter */
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
map_so_lib( interp, &ld_so_map );
/* store pointer to the preload info into the appropriate main binary variable */
wine_main_preload_info = find_symbol( main_binary_map.l_phdr, main_binary_map.l_phnum,
"wine_main_preload_info" );
if (wine_main_preload_info) *wine_main_preload_info = preload_info;
else wld_printf( "wine_main_preload_info not found\n" );
set_auxiliary( av, AT_PHDR, (unsigned long)main_binary_map.l_phdr );
set_auxiliary( av, AT_PHNUM, main_binary_map.l_phnum );
set_auxiliary( av, AT_BASE, ld_so_map.l_addr );
set_auxiliary( av, AT_ENTRY, main_binary_map.l_entry );
#ifdef DUMP_AUX_INFO
wld_printf("New auxiliary info:\n");
dump_auxiliary( av );
wld_printf("jumping to %x\n", ld_so_map.l_entry);
#endif
return (void *)ld_so_map.l_entry;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment