/* i386-linux.elf.execve-main.c -- generic stub loader for Linux using execve()

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   Copyright (C) Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>

   John F. Reiser
   <jreiser@users.sourceforge.net>
 */


#include "include/linux.h"


/*************************************************************************
// configuration section
**************************************************************************/

// mmap() the temporary output file
#define USE_MMAP_FO 1


/*************************************************************************
// file util
**************************************************************************/

struct Extent {
    int  size;  // must be first to match size[0] uncompressed size
    char *buf;
};


#if !(USE_MMAP_FO)
#if 1
static __inline__ int xwrite(int fd, const void *buf, int count)
{
    // note: we can assert(count > 0);
    do {
        int n = write(fd, buf, count);
        if (n == -EINTR)
            continue;
        if (n <= 0)
            break;
        buf += n;               // gcc extension: add to void *
        count -= n;
    } while (count > 0);
    return count;
}
#else
#define xwrite(fd,buf,count)    ((count) - write(fd,buf,count))
#endif
#endif /* !USE_MMAP_FO */


/*************************************************************************
// util
**************************************************************************/

#if 1

extern char *
__attribute__((regparm(2), stdcall))  // be ruthless
upx_itoa(unsigned long v, char *buf);

#else

// Some versions of gcc optimize the division and/or remainder using
// a multiplication by (2**32)/10, and use a relocatable 32-bit address
// to reference the constant.  We require no relocations because we move
// the code at runtime.  See upx_itoa.S for replacement [also smaller.]
static char *upx_itoa(unsigned long v, char *buf)
{
//    const unsigned TEN = 10;
    volatile unsigned TEN = 10;  // an ugly way to achieve no relocation
    char *p = buf;
    {
        unsigned long k = v;
        do {
            p++;
            k /= TEN;
        } while (k > 0);
    }
    buf = p;
    *p = 0;
    {
        unsigned long k = v;
        do {
            *--p = '0' + k % TEN;
            k /= TEN;
        } while (k > 0);
    }
    return buf;
}

#endif


static uint32_t ascii5(char *p, uint32_t v, unsigned n)
{
    do {
        unsigned char d = v % 32;
        if (d >= 26) d -= 43;       // 43 == 'Z' - '0' + 1
        *--p = (d += 'A');
        v /= 32;
    } while (--n > 0);
    return v;
}


#if defined(__i386__)
#  define SET2(p, c0, c1) \
        * (unsigned short *) (p) = ((c1)<<8 | (c0))
#  define SET4(p, c0, c1, c2, c3) \
        * (uint32_t *) (p) = ((c3)<<24 | (c2)<<16 | (c1)<<8 | (c0))
#  define SET3(p, c0, c1, c2) \
        SET4(p, c0, c1, c2, 0)
#else
#  define SET2(p, c0, c1) \
        (p)[0] = c0, (p)[1] = c1
#  define SET3(p, c0, c1, c2) \
        (p)[0] = c0, (p)[1] = c1, (p)[2] = c2
#  define SET4(p, c0, c1, c2, c3) \
        (p)[0] = c0, (p)[1] = c1, (p)[2] = c2, (p)[3] = c3
#endif


// go_self is a separate subroutine to spread the burden of local arrays.
// Otherwise the size of the stack frame in upx_main exceeds 128 bytes,
// which causes too many offsets to expand from 1 byte to 4.

static int
#if (ACC_CC_GNUC >= 0x030300)
__attribute__((__noinline__, __used__, __stdcall__))
#endif
go_self(char const *tmpname, char *argv[], char *envp[])
{
    // Old FreeBSD does not have /proc/self, so use /proc/<pid> instead.

    // Open the temp file.
    int const fdi = open(tmpname, O_RDONLY, 0);

    if (0 <= fdi) {
        // 17 chars for "/proc/PPPPP/fd/XX" should be enough, but we
        // play safe in case there will be 32-bit pid_t at some time.
        //char procself_buf[17+1];
        char procself_buf[31+1];

        // Compute name of temp fdi.
        SET4(procself_buf + 0, '/', 'p', 'r', 'o');
        SET4(procself_buf + 4, 'c', '/',  0 ,  0 );
        {
            char *const procself = upx_itoa(getpid(), procself_buf + 6);
            SET4(procself, '/', 'f', 'd', '/');
            upx_itoa(fdi, procself + 4);
        }

        // Check for working /proc/self/fd/X by accessing the
        // temp file again, now via temp fdi.
        if (UPX2 == (unsigned) access(procself_buf, R_OK | X_OK)) {
            // Now it's safe to unlink the temp file (as it is still open).
            unlink(tmpname);
            // Set the file close-on-exec.
            fcntl(fdi, F_SETFD, FD_CLOEXEC);
            // Execute the original program via /proc/self/fd/X.
            execve(procself_buf, argv, envp);
            // NOTE: if we get here we've lost.

            // 2008-March: Ubuntu 7.10 linux-image-2.6.22-14.52-generic
            // and SuSE 10.3 have a patch to fs/dcache.c which causes
            // execve() [above] to fail with ENOENT.
            // Fedora kernel-2.6.22.14-72.fc6 omits the patch, and works:
// https://sourceforge.net/tracker/?func=detail&atid=102331&aid=1825006&group_id=2331

            // Now we must re-create the file; it was unlink()ed!
            // link(procself_buf, tmpname) gets EXDEV (cross-device link).
            // Luckily we still have an open fdi to the file.
            // Errors from {lseek, mmap, open, write} are all caught
            // by the one check on write().
            unsigned const lenf = lseek(fdi, 0, SEEK_END);
            char *const buf = mmap((char *)0, lenf, PROT_READ|PROT_WRITE,
                MAP_PRIVATE, fdi, 0);
            int const fdo = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, 0700);
            if (lenf!=(unsigned)write(fdo, buf, lenf)) {
                exit(127);
            }
            close(fdo);
        }

        // The proc filesystem isn't working. No problem.
        close(fdi);
    }
    return fdi;
}


/*************************************************************************
// UPX & NRV stuff
**************************************************************************/

typedef void f_unfilter(
    nrv_byte *,  // also addvalue
    nrv_uint,
    unsigned cto8  // junk in high 24 bits
);
typedef int f_expand(
    const nrv_byte *src, nrv_uint  src_len,
          nrv_byte *dst, nrv_uint *dst_len, int method );


void my_bkpt(int rv)
{
#if defined(__i386__)  //{
    __asm__("int3");
#endif  //}
    (void)rv;
}
#define error1(rv) {my_bkpt(rv); goto error1;}
#define error(rv)  {my_bkpt(rv); goto error;}

/*************************************************************************
// upx_main - called by our entry code
//
// This function is optimized for size.
**************************************************************************/

void upx_main(
    struct Extent xi,
    f_expand *const f_decompress,
    int junk2,
    f_unfilter *const f_unf,
    char *envp[],
    char *argv[],
    int argc
) __asm__("upx_main");
void upx_main(
    struct Extent xi,
    f_expand *const f_decompress,
    int junk,
    f_unfilter *const f_unf,
    char *envp[],
    char *argv[],
    int argc
)
{
    // file descriptor
    int fdo;

    // decompression buffer
    unsigned char *buf;

    char *tmpname;
    struct p_info header;

    // temporary file name
    char tmpname_buf[20];

    (void)junk;

    //
    // ----- Step 0: set /proc/self using /proc/<pid> -----
    //

    //personality(PER_LINUX);


    //
    // ----- Step 1: prepare input file -----
    //

    // Read header.
    {
        register char *__d0, *__d1;
        __asm__ __volatile__( "movsl; movsl; movsl"
            : "=&D" (__d0), "=&S" (__d1)
            : "0" (&header), "1" (xi.buf)
            : "memory");
        xi.buf   = __d1;
        xi.size -= sizeof(header);
    }

    // Paranoia. Make sure this is actually our expected executable
    // by checking the random program id. (The id is both stored
    // in the header and patched into this stub.)
    if (header.p_progid != UPX3)
        error1(11);


    //
    // ----- Step 2: prepare temporary output file -----
    //

    tmpname = tmpname_buf;
    SET4(tmpname + 0, '/', 't', 'm', 'p');
    SET4(tmpname + 4, '/', 'u', 'p', 'x');

    // Compute name of temporary output file in tmpname[].
    // Protect against Denial-of-Service attacks.
    {
        char *p = tmpname_buf + sizeof(tmpname_buf) - 1;

        // Compute the last 4 characters (20 bits) from getpid().
        uint32_t r = ascii5(p, (uint32_t)getpid(), 4); *p = '\0'; p -= 4;

        // Provide 4 random bytes from our program id.
        r ^= header.p_progid;
        // Mix in 4 runtime random bytes.
        // Don't consume precious bytes from /dev/urandom.
        {
#if 1
            struct timeval tv;
            gettimeofday(&tv, 0);
            r ^= (uint32_t) tv.tv_sec;
            r ^= ((uint32_t) tv.tv_usec) << 12;      // shift into high-bits
#else
            // using adjtimex() may cause portability problems
            struct timex tx;
            adjtimex(&tx);
            r ^= (uint32_t) tx.time.tv_sec;
            r ^= ((uint32_t) tx.time.tv_usec) << 12; // shift into high-bits
            r ^= (uint32_t) tx.errcnt;
#endif
        }
        // Compute 7 more characters from the 32 random bits.
        ascii5(p, r, 7);
    }

    // Just in case, remove the file.
    {
        int err = unlink(tmpname);
        if (err != -ENOENT && err != 0)
            error1(12);
    }

    // Create the temporary output file.
#if (USE_MMAP_FO)
    fdo = open(tmpname, O_RDWR | O_CREAT | O_EXCL, 0700);
#else
    fdo = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, 0700);
#endif
#if 0
    // Save some bytes of code - the ftruncate() below will fail anyway.
    if (fdo < 0)
        error(13);
#endif

    // Set expected uncompressed file size.
    if (ftruncate(fdo, header.p_filesize) != 0)
        error(14);


    //
    // ----- Step 3: setup memory -----
    //

#if (USE_MMAP_FO)
    // FIXME: packer could set length
    // Ask for one page more to reserve placement.
    buf = (unsigned char *)mmap(0, header.p_filesize -PAGE_MASK,
        PROT_READ | PROT_WRITE, MAP_SHARED, fdo, 0);
    if ((unsigned long) buf >= (unsigned long) -4095)
        error(15);

    // Decompressor can overrun the output by 3 bytes.
    // Defend against SIGSEGV by using a scratch page.
    // FIXME: packer could set address delta
    mmap(buf + (PAGE_MASK & (header.p_filesize + ~PAGE_MASK)),
        -PAGE_MASK, PROT_READ | PROT_WRITE,
        MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
#else
    // Temporary decompression buffer.
    // FIXME: packer could set length
    buf = mmap(0, (header.p_blocksize + OVERHEAD + ~PAGE_MASK) & PAGE_MASK,
        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
    if ((unsigned long) buf >= (unsigned long) -4095)
        error(16);
#endif

    //
    // ----- Step 4: decompress blocks -----
    //

    for (;;)
    {
        struct b_info h;

        // Read and check block sizes.
        {
            register char *__d0, *__d1;
            __asm__ __volatile__( "movsl; movsl; movsl"
                : "=&D" (__d0), "=&S" (__d1)
                : "0" (&h), "1" (xi.buf)
                : "memory");
            xi.buf   = __d1;
            xi.size -= sizeof(h);
        }
        if (h.sz_unc == 0)                      // uncompressed size 0 -> EOF
        {
            if (h.sz_cpr != UPX_MAGIC_LE32)     // h.sz_cpr must be h->magic
                error(17);
            if (header.p_filesize != 0)         // all bytes must be written
                error(18);
            break;
        }
        //   Note: if sz_unc == sz_cpr then the block was not
        //   compressible and is stored in its uncompressed form.

        if (h.sz_cpr > h.sz_unc || h.sz_cpr > header.p_blocksize)
            error(19);
        // Now we have:
        //   assert(h.sz_cpr <= h.sz_unc);
        //   assert(h.sz_unc > 0 && h.sz_unc <= blocksize);
        //   assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize);

        if (h.sz_cpr < h.sz_unc) { // Decompress block.
            nrv_uint out_len = h.sz_unc;  // EOF for lzma
            int i = (*f_decompress)((unsigned char *)xi.buf, h.sz_cpr, buf,
                &out_len, *(int *)(void *)&h.b_method);
            if (i != 0 || out_len != (nrv_uint)h.sz_unc)
                error(20);
            // Right now, unfilter is combined with decompression.
            // (*f_unfilter)(buf, out_len, cto8);
            (void)f_unf;
        }
        else
        {
            // Incompressible block
#if (USE_MMAP_FO)
            //memcpy(buf, xi.buf, h.sz_unc);
            register unsigned long int __d0, __d1, __d2;
            __asm__ __volatile__( "rep; movsb"
                : "=&c" (__d0), "=&D" (__d1), "=&S" (__d2)
                : "0" (h.sz_unc), "1" (buf), "2" (xi.buf)
                : "memory");
#endif
        }

#if (USE_MMAP_FO)
        // unmap part of the output
        munmap(buf, h.sz_unc);
        buf     += h.sz_unc;
#else
        // write output file
        if (xwrite(fdo, buf, h.sz_unc) != 0)
            error(21);
#endif

        header.p_filesize -= h.sz_unc;

        xi.buf  += h.sz_cpr;
        xi.size -= h.sz_cpr;

        if (xi.size < 0) {
// error exit is here in the middle to keep the jumps short.
        error:
            (void) unlink(tmpname);
        error1:
            // Note: the kernel will close all open files and
            //       unmap any allocated memory.
            for (;;)
                (void) exit(127);
        }
    }

    //
    // ----- Step 5: release resources -----
    //


#if !(USE_MMAP_FO)
    // Free our temporary decompression buffer.
    munmap(buf, malloc_args.ma_length);
#endif

    if (close(fdo) != 0)
        error(22);


    //
    // ----- Step 6: try to start program via /proc/self/fd/X -----
    //

    // Many thanks to Andi Kleen <ak@muc.de> and
    // Jamie Lokier <nospam@cern.ch> for this nice idea.

    if (0 > go_self(tmpname, argv, envp))
        error(23);


    //
    // ----- Step 7: start program in /tmp  -----
    //

    // Fork off a subprocess to clean up.
    // We have to do this double-fork trick to keep a zombie from
    // hanging around if the spawned original program doesn't check for
    // subprocesses (as well as to prevent the real program from getting
    // confused about this subprocess it shouldn't have).
    // Thanks to Adam Ierymenko <api@one.net> for this solution.

    if (fork() == 0)
    {
        if (fork() == 0)
        {
            // Sleep 3 seconds, then remove the temp file.
            struct timespec ts; ts.tv_sec = UPX4; ts.tv_nsec = 0;
            nanosleep(&ts, 0);
            unlink(tmpname);
        }
        exit(0);
    }

    // Wait for the first fork()'d process to die.
    waitpid(-1, (int *)0, 0);

    // Execute the original program.
    (void)argc;
    execve(tmpname, argv, envp);


    //
    // ----- Step 8: error exit -----
    //

    // If we return from execve() there was an error. Give up.
    error(24);
}

/* vim:set ts=4 sw=4 et: */
