inheriting exec-shield avoidance

Mike McCormack mike at codeweavers.com
Thu Apr 8 09:47:20 CDT 2004


Hi Peter,

I'm not familiar with the using "setarch i386" to solve the problem...

I've also been working on the exec-shield problem over the last couple 
of days.  My solution is similar to Mike Hearn's approach, but faster 
and more compact, since it doesn't require loading of libc twice or 
static linking with glibc.  The idea is to use a staticly linked ELF 
object, reserve memory and then load the target dynamicly linked object.

It seems to do what I want, but will require a patch or two to wine to 
use MAP_FIXED when mapping the first megabyte of address space, and to 
make sure newly forked processes are forked with it as the (pre)loader.

I've attached the source code.  The idea is to run it like this:

wld /home/mike/wine/loader/wine-pthread my.exe

Mike

Peter Riocreux wrote:
> I am intermittently trying to get Wine to play nicely with a big EDA
> tool, and it is doing not too badly today with the 20040309 snapshot
> on Fedora Core 1.
> 
> The thing that I think is stopping it working is that whatever is done
> to stack-shield by the prepending "setarch i386" on invocation is not
> inherited by the .exe that the program calls. Net result is that I get
> the "..... security-patched kernel ?" error message from the child
> process instead of the parent. Progress of a sort.
> 
> Is there anything, short of turning off exec-shield entirely, that can
> be done about this?

-------------- next part --------------
CFLAGS=-Wall
CC=gcc
OBJS=wld.o sysdeps.o

all: wld test

wld: wld.o sysdeps.o
	$(CC) -nostartfiles -nodefaultlibs -static -Wl,"-Ttext=0x7fff1000" -o $@ $(OBJS)

$(OBJS:.c=.o): sysdeps.h

test: testmain ld-mine.so

testmain: testmain.o
	$(CC) -Wl,--dynamic-linker=ld-mine.so -o $@ testmain.o

ld-mine.so: testld.o
	$(CC) -nostartfiles -nodefaultlibs -static -Wl,"-Ttext=0x3eee0000" -o $@ testld.o


clean:
	rm -f wld $(OBJS) ld-mine.so testld.o testmain testmain.o
-------------- next part --------------
/* 
   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. 
 */

#include <syscall.h>
#include "sysdeps.h"

/*
 * 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"
        "\tpop %ebx\n"
        "\tadd $4,%esp\n"
        "\tdec %ebx\n"
        "\tpush %ebx\n"
        "\tpush %eax\n"
        "\txor %eax,%eax\n"
        "\txor %ebx,%ebx\n"
        "\txor %ecx,%ecx\n"
	"\tret\n"
);

/*
 *   various Linux system calls that we need
 */
void wld_exit(int val)
{
    asm( "\tint $0x80\n"
       :
       :"a"(SYS_exit), "b"(val)
       );
}

int wld_close(int fd)
{
    int r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_close), "b"(fd)
       );
    return r;
}

void* wld_brk(void *end)
{
    void* r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_brk), "b"(end)
       );
    return r;
}

#if 0
void* wld_mmap( void *addr, unsigned int len, unsigned int prot,
                unsigned int flags, int fd, unsigned int pgoff )
{
    void* r;
    pgoff = pgoff >> 12;
    asm("\tpush %ebp\n"
        "\tmov %6, %ebp\n"
        "\tint $0x80\n"
        "\tpop %ebp\n"
       : "=a"(r)
       :"a"(SYS_mmap2), "b"(addr), "c"(len), 
         "d"(prot), "S"(flags), "D"(fd), "n"(pgoff)
       );
    return r;
}
#endif
#if 0
void* wld_mmap( void *addr, unsigned int len, unsigned int prot,
                unsigned int flags, int fd, unsigned int pgoff );
__asm (
	"\t.align 4\n"
	"\t.global wld_mmap\n"
	"\t.type wld_mmap, at function\n"
"wld_mmap:\n"
        "\tpush %ebx\n"
        "\tpush %ecx\n"
        "\tpush %edx\n"
        "\tpush %esi\n"
        "\tpush %edi\n"
        "\tpush %ebp\n"
	"\tmovl $192, %eax\n"
	"\tmovl 28(%esp), %ebx\n"
	"\tmovl 32(%esp), %ecx\n"
	"\tmovl 36(%esp), %edx\n"
	"\tmovl 40(%esp), %esi\n"
	"\tmovl 44(%esp), %edi\n"
	"\tmovl 48(%esp), %ebp\n"
        "\tshrl $12, %ebp\n"
	"\tint $0x80\n"
        "\tpop %ebp\n"
        "\tpop %edi\n"
        "\tpop %esi\n"
        "\tpop %edx\n"
        "\tpop %ecx\n"
        "\tpop %ebx\n"
	"\tret\n"
"wld_mmap_end:\n"
	"\t.size wld_mmap, wld_mmap_end - wld_mmap\n"
);
#endif
#if 1
void* wld_mmap( void *addr, unsigned int len, unsigned int prot,
                unsigned int flags, int fd, unsigned int pgoff )
{
    void *r;
    asm( 
        "\tint $0x80\n"
	:"=a"(r)
	:"a"(SYS_mmap), "b"(&addr)
        :"%ecx","%edx"
    );
    return r;
}

#endif

int wld_mprotect( const void* address, size_t len, int prot )
{
    int r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_mprotect), "b"(address), "c"(len), "d"(prot)
       );
    return r;
}

int wld_read( int fd, void *buffer, int len )
{
    int r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_read), "b"(fd), "c"(buffer), "d"(len)
       );
    return r;
}

int wld_write( int fd, void *buffer, int len )
{
    int r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_write), "b"(fd), "c"(buffer), "d"(len)
       );
    return r;
}

int wld_open( char *file, int flags, int mode )
{
    int r;
    asm( "\tint $0x80\n"
       : "=a"(r)
       :"a"(SYS_open), "b"(file), "c"(flags), "d"(mode)
       );
    return r;
}


-------------- next part --------------
/* 
   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. 
 */

#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/mman.h>

#define LD_SO "/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

void wld_exit(int val);
int wld_close(int fd);
void* wld_brk(void *end);
void* wld_mmap( void *addr, unsigned int len, unsigned int prot,
                unsigned int flags, int fd, unsigned int pgoff );
int wld_mprotect( const void* address, size_t len, int prot );
int wld_read( int fd, void *buffer, int len );
int wld_write( int fd, void *buffer, int len );
int wld_open( char *file, int flags, int mode );

#define __close wld_close
#define __mmap wld_mmap
#define __mprotect wld_mprotect
#define memset __builtin_memset

#define GL(x) x

extern unsigned int GL(dl_pagesize);

-------------- next part --------------
/* 
   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 a small program that reserves memory that is
 *  important to Wine, and then loads the Wine executable and the shared object
 *  loader (ELF interpreter), usually ld-linux.so.2
 *
 * As Wine is i386 specific, we are not too worried about portability issues,
 *  but will do our best to keep the system dependencies (syscalls) seperate to 
 *  the rest (ELF loader and printf) part of the program.
 *
 * Since we want to be loaded first, we will not link to any other libraries,
 *  including the standard C library (libc) or the dynamic linker itself.  Do
 *  do that this program needs to be fully self contained, implementing it's own
 *  library function and system call interface.  Since we only need enough
 *  functionality to load an binary, this isn't a problem.
 *
 * This program will be invoked as follows:
 * 
 *  wld <full path to ELF executable to be loaded> <args...>
 *
 * We will try to set up the stack an memory area so that the program that
 *  loads after us (eg. the wine binary) never knows we were here, except that
 *  areas of memory it needs are already magically reserved.
 *
 * The following memory areas are important to Wine:
 *  0x00000000 - the DOS area
 *  0x00400000 - the standard Win32 load address
 *  0x65430000 - the shared heap at
 *
 * Before passing control to the loaded binary, we will fix the argc and argv[]
 *  values passed to the program.
 *
 * There are a few things that will be different after this program passes
 * control of execution to the loaded binary:
 *  1) The _ variable in the environment (will be the name of this exe)
 *  2) The brk area returned by brk(2) 
 *  3) This program will still be mapped into memory.
 *  4) The command line kept by the kernel in kernel memory (/proc/self/cmdline)
 *
 * Limitations:
 *  - The name of the target ELF executable needs to be a full path name
 */

/*
 * 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 "sysdeps.h"

#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/mman.h>

#include <syscall.h>
#include <elf.h>

/* debugging */
#undef DUMP_SEGMENTS
#undef DUMP_ARG
#undef DUMP_AUX_INFO

unsigned int GL(dl_pagesize) = 0x0000;

/*
 * 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 );
    wld_write(2, buffer, n);
}

void wld_strncpy(char *dest, char *src, int count )
{
    while( (*dest++ = *src++) && --count )
        ;
    *dest = 0;
}

/*
 *  Dump interesting bits of the ELF auxv_t structure that is passed
 *   as the 4th parameter to the _start function
 */
void wld_dump_auxilary( ElfW(auxv_t) *av )
{
  for (  ; av->a_type != AT_NULL; av++)
    switch (av->a_type)
      {
      case AT_PAGESZ:
        GL(dl_pagesize) = av->a_un.a_val;
#ifndef DUMP_AUX_INFO
        break;
#else
        wld_printf("AT_PAGESZ = %x\n",av->a_un.a_val);
        break;
      case AT_PHDR:
        wld_printf("AT_PHDR = %x\n",av->a_un.a_ptr);
        break;
      case AT_PHNUM:
        wld_printf("AT_PHNUM = %x\n",av->a_un.a_val);
        break;
      case AT_ENTRY:
        wld_printf("AT_ENTRY = %x\n",av->a_un.a_val);
        break;
      case AT_BASE:
        wld_printf("AT_BASE = %x\n",av->a_un.a_val);
        break;
#endif
      }
}

/*
 * wld_set_auxilary
 *
 * Set a field of the auxillary structure
 */
void wld_set_auxilary( ElfW(auxv_t) *av, int type, long int val )
{
  for ( ; av->a_type != AT_NULL; av++)
    if( av->a_type == type )
      av->a_un.a_val = val;
}

/*
 * wld_map_so
 *
 * modelled after _dl_map_object_from_fd() from glibc-2.3.1/elf/dl-load.c
 *
 * This function maps the segments from an ELF object, and optionally
 *  stores information about the mapping into the auxv_t structure.
 */
void* wld_map_so( int fd, void* buf, size_t buflen, ElfW(auxv_t)* av, char* interp, int interp_sz )
{
    ElfW(Ehdr) *header = buf;
    ElfW(Phdr) *phdr, *ph;
    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;
    } lmap,*l = &lmap;
    /* 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*) (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");
        wld_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:
          {
              char *str = (char *) buf+ph->p_offset;
              wld_strncpy(interp, str, interp_sz);
          }
          break;

        case PT_SHLIB:
        case PT_NOTE:
        case PT_GNU_EH_FRAME:
          break;

        default:
          wld_printf("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");
      wld_exit(1);
    }

    (ElfW(Addr)) l->l_phdr += l->l_addr;
    (ElfW(Addr)) l->l_entry += l->l_addr;

    if( !interp )
       wld_set_auxilary( av, AT_BASE, l->l_addr );
    else
    {
       wld_set_auxilary( av, AT_PHDR, (unsigned long)l->l_phdr );
       wld_set_auxilary( av, AT_PHNUM, l->l_phnum );
       wld_set_auxilary( av, AT_ENTRY, l->l_entry );
    }

    return (void*)l->l_entry;
}

/*
 *  wld_reserve_wine_memory
 *
 *  This is the reason we are here.
 *  We want to reserve address space so that the kernel doesn't
 *   map stuff there.  We do this by getting in first before
 *   anything else is mapped (except this program) and reserve
 *   the memory we want.
 */
void wld_reserve_wine_memory()
{
    wld_mmap((void*)0x00000000, 0x3fffffff, PROT_NONE, 
              MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
    /* wld_mmap((void*)0x65430000, 0x00400000, PROT_NONE, 
              MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); */
}

/*
 *  wld_start
 *
 *  Repeat the actions the kernel would do when loading a dynamically linked .so
 *   Load the ELF interperter ld-linux.so and then binary itself.
 *
 *  Note, we assume that the binary is a dynamically linked ELF shared object
 *   and that ld-linux.so is its ELF interpreter.
 */
void* wld_start( int argc )
{
    int fd;
    unsigned char buf[0x800], interp[0x100];
    void *start;
    char **argv = (char**) &argc, **p;
    ElfW(auxv_t)* av;

    if( argc < 2 )
    {
        wld_printf( "need to specify a program name\n");
        wld_exit(1);
    }

    /* skip over the parameters */
    p = ++argv;
#ifdef DUMP_ARG
    {
        int i;
        for( i = 0; i<argc; i++ )
            wld_printf("argv[%x] = %s\n", i, *p++);
        if(*p++)
            wld_printf("argv[argc] not NULL!!\n");
    }
#else
    p+=(argc+1);
#endif

    /* skip over the environment */
    while( *p++ )
        ;

    av = (ElfW(auxv_t)*) p;
    wld_dump_auxilary( av );

    /* reserve memory that Wine needs */
    wld_reserve_wine_memory();

    /* load the binary itself */
    fd = wld_open( argv[1], O_RDONLY, 0);
    if( fd < 0 )
    {
        wld_printf( "error: fd = %x\n", fd);
        wld_exit(1);
    }
    wld_read( fd, buf, sizeof buf );
    interp[0] = 0;
    start = wld_map_so( fd, buf, sizeof buf, av, interp, sizeof interp );
    if( !start )
        wld_exit(0);
    wld_close( fd );

    if( interp[0] )
    {
        /* load the dynamic loader */
        fd = wld_open( interp, O_RDONLY, 0);
        if( fd < 0 )
        {
            wld_printf( "error: fd = %x\n", fd);
            wld_exit(1);
        }
        wld_read( fd, buf, sizeof buf );
        start = wld_map_so( fd, buf, sizeof buf, av, NULL, 0);
        wld_close( fd );
    }
    if( !start )
        wld_exit(0);

#ifdef DUMP_AUX_INFO
    wld_printf("New auxillary info:\n");
    wld_dump_auxilary( av );
    wld_printf("jumping to %x\n", start);
#endif

    return start;
}


More information about the wine-devel mailing list