/* p_lx_exc.cpp --

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   Copyright (C) Laszlo Molnar
   Copyright (C) John F. Reiser
   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 "conf.h"

#include "file.h"
#include "filter.h"
#define WANT_EHDR_ENUM 1
#include "p_elf_enum.h"
#include "linker.h"
#include "packer.h"
#include "p_elf.h"
#include "p_unix.h"
#include "p_lx_exc.h"

#define PT_LOAD     Elf32_Phdr::PT_LOAD
#define PT_DYNAMIC  Elf32_Phdr::PT_DYNAMIC
#if 0 // UNUSED
#define DT_NULL     Elf32_Dyn::DT_NULL
#define DT_NEEDED   Elf32_Dyn::DT_NEEDED
#define DT_STRTAB   Elf32_Dyn::DT_STRTAB
#define DT_STRSZ    Elf32_Dyn::DT_STRSZ
#endif


/*************************************************************************
// linux/386 (generic "execve" format)
**************************************************************************/

PackLinuxI386::PackLinuxI386(InputFile *f) : super(f),
    ei_osabi(Elf32_Ehdr::ELFOSABI_LINUX), osabi_note(nullptr)
{
    bele = &N_BELE_RTP::le_policy;
}

PackBSDI386::PackBSDI386(InputFile *f) : super(f)
{
    // Shell scripts need help specifying the target operating system.
    // Elf input will override this with .e_ident[EI_OSABI] or PT_NOTE.
    // [2006-09-27: Today's only runtime stub for shell is for linux.]
    if (Elf32_Ehdr::ELFOSABI_LINUX==opt->o_unix.osabi0) {
        // Disallow an incompatibility.
        ei_osabi = Elf32_Ehdr::ELFOSABI_NONE;
    }
    else {
        ei_osabi = opt->o_unix.osabi0;  // might be ELFOSABI_NONE
    }
}

static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i386-linux.elf.execve-entry.h"
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i386-linux.elf.execve-fold.h"

static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i386-bsd.elf.execve-entry.h"
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/i386-bsd.elf.execve-fold.h"


const int *PackLinuxI386::getCompressionMethods(int method, int level) const
{
    return Packer::getDefaultCompressionMethods_le32(method, level);
}

const int *PackLinuxI386::getFilters() const
{
    static const int filters[] = {
        0x49, 0x46,
        0x26, 0x24, 0x11, 0x14, 0x13, 0x16, 0x25, 0x15, 0x12,
#if 0
// 0x80..0x87 are regarded as "untested".
        0x83, 0x86, 0x80, 0x84, 0x87, 0x81, 0x82, 0x85,
        0x24, 0x16, 0x13, 0x14, 0x11, 0x25, 0x15, 0x12,
#endif
    FT_END };
    return filters;
}

static void
set_stub_brk(Elf_LE32_Phdr *const phdr1, unsigned brka)
{
#define PAGE_MASK (~0ul<<12)
        // linux-2.6.14 binfmt_elf.c: SIGKILL if (0==.p_memsz) on a page boundary
        unsigned const brkb = brka | ((0==(~PAGE_MASK & brka)) ? 0x20 : 0);
        phdr1->p_type = PT_LOAD;  // be sure
        phdr1->p_offset = ~PAGE_MASK & brkb;
        phdr1->p_vaddr = brkb;
        phdr1->p_paddr = brkb;
        phdr1->p_filesz = 0;
        phdr1->p_memsz =  0;
        if (0==phdr1->p_flags) {
            phdr1->p_flags = Elf32_Phdr::PF_R|Elf32_Phdr::PF_W;
        }
        if (0==phdr1->p_align) {
            phdr1->p_align = 0x1000;
        }
#undef PAGE_MASK
}

void
PackLinuxI386::generateElfHdr(
    OutputFile *fo,
    void const *proto,
    unsigned const brka
)
{
    cprElfHdr2 *const h2 = (cprElfHdr2 *)(void *)&elfout;
    cprElfHdr3 *const h3 = (cprElfHdr3 *)(void *)&elfout;
    memcpy(h3, proto, sizeof(*h3));  // reads beyond, but OK

    assert(h2->ehdr.e_phoff     == sizeof(Elf32_Ehdr));
    assert(h2->ehdr.e_shoff     == 0);
    assert(h2->ehdr.e_ehsize    == sizeof(Elf32_Ehdr));
    assert(h2->ehdr.e_phentsize == sizeof(Elf32_Phdr));
    assert(h2->ehdr.e_shnum     == 0);

#if 0  //{
    unsigned identsize;
    char const *const ident = getIdentstr(&identsize);
#endif  //}
    h2->phdr[0].p_filesz = sizeof(*h2);  // + identsize;
    h2->phdr[0].p_memsz  = h2->phdr[0].p_filesz;

    // Info for OS kernel to set the brk()
    if (brka) {
        set_stub_brk(&h2->phdr[1], brka);
    }

    if (ph.format==UPX_F_LINUX_i386
    ||  ph.format==UPX_F_LINUX_SH_i386
    ||  ph.format==UPX_F_BSD_i386
    ) {
        // SELinux, PAx, grSecurity demand no PF_W if PF_X.
        // kernel-2.6.12-2.3.legacy_FC3 has a bug which demands
        // a PT_LOAD with PF_W, else SIGSEGV when clearing page fragment
        // on low page of ".bss", which is the high page of .text.
        // So the minimum number of PT_LOAD is 2.
        assert(h2->ehdr.e_phnum==2);
        memset(&h2->linfo, 0, sizeof(h2->linfo));
        fo->write(h2, sizeof(*h2));
    }
    else if (ph.format==UPX_F_LINUX_ELFI_i386) {
        assert(h3->ehdr.e_phnum==3);
        memset(&h3->linfo, 0, sizeof(h3->linfo));
        fo->write(h3, sizeof(*h3));
    }
    else {
        assert(false);  // unknown ph.format, PackUnix::generateElfHdr
    }
}

void
PackLinuxI386::pack1(OutputFile *fo, Filter &)
{
    // create a pseudo-unique program id for our paranoid stub
    progid = getRandomId();

    generateElfHdr(fo, stub_i386_linux_elf_execve_fold, 0);
}

void
PackBSDI386::pack1(OutputFile *fo, Filter &)
{
    // create a pseudo-unique program id for our paranoid stub
    progid = getRandomId();

    generateElfHdr(fo, stub_i386_bsd_elf_execve_fold, 0);
}

void
PackLinuxI386::pack4(OutputFile *fo, Filter &ft)
{
    overlay_offset = sizeof(elfout.ehdr) +
        (elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) +
        sizeof(l_info) +
        ((elfout.ehdr.e_phnum==3) ? (unsigned) elfout.phdr[2].p_memsz : 0) ;
    unsigned nw = fo->getBytesWritten();
    elfout.phdr[0].p_filesz = nw;
    nw = 0u-((0u-elfout.phdr[0].p_align) & (0u-nw));  // ALIGN_UP
    super::pack4(fo, ft);  // write PackHeader and overlay_offset
    set_stub_brk(&elfout.phdr[1], nw + elfout.phdr[0].p_vaddr);

#if 0  // {
    // /usr/bin/strip from RedHat 8.0 (binutils-2.13.90.0.2-2)
    // generates a 92-byte [only] output, because the "linking view"
    // is empty.  This code supplies a "linking view".
    // However, 'strip' then generates _plausible_ junk that gets
    // "Illegal instruction"  because 'strip' changes p_hdr[1].p_align,
    // .p_offset, and .p_vaddr incorrectly.  So the "cure" is worse than
    // the disease.  It is obvious that a 92-byte file is bad,
    // but it is not obvious that the changed .p_align is bad.
    // Also, having a totally empty "linking view" is easier for 'strip'
    // to fix: just detect that, and do nothing.
    // So, we don't use this code for now [2003-01-11].

    // Supply a "linking view" that covers everything,
    // so that 'strip' does not omit everything.
    Elf_LE32_Shdr shdr;
    // The section header string table.
    char const shstrtab[] = "\0.\0.shstrtab";

    unsigned eod = elfout.phdr[0].p_filesz;
    elfout.ehdr.e_shoff = eod;
    elfout.ehdr.e_shentsize = sizeof(shdr);
    elfout.ehdr.e_shnum = 3;
    elfout.ehdr.e_shstrndx = 2;

    // An empty Elf32_Shdr for space as a null index.
    memset(&shdr, 0, sizeof(shdr));
    shdr.sh_type = Elf32_Shdr::SHT_NULL;
    fo->write(&shdr, sizeof(shdr));

    // Cover all the bits we need at runtime.
    memset(&shdr, 0, sizeof(shdr));
    shdr.sh_name = 1;
    shdr.sh_type = Elf32_Shdr::SHT_PROGBITS;
    shdr.sh_flags = Elf32_Shdr::SHF_ALLOC;
    shdr.sh_addr = elfout.phdr[0].p_vaddr;
    shdr.sh_offset = overlay_offset;
    shdr.sh_size = eod - overlay_offset;
    shdr.sh_addralign = 4096;
    fo->write(&shdr, sizeof(shdr));

    // A section header for the section header string table.
    memset(&shdr, 0, sizeof(shdr));
    shdr.sh_name = 3;
    shdr.sh_type = Elf32_Shdr::SHT_STRTAB;
    shdr.sh_offset = 3*sizeof(shdr) + eod;
    shdr.sh_size = sizeof(shstrtab);
    fo->write(&shdr, sizeof(shdr));

    fo->write(shstrtab, sizeof(shstrtab));
#endif  // }


    // Cannot pre-round .p_memsz.  If .p_filesz < .p_memsz, then kernel
    // tries to make .bss, which requires PF_W.
    // But strict SELinux (or PaX, grSecurity) disallows PF_W with PF_X.
#if 0  /*{*/
#undef PAGE_MASK
#define PAGE_MASK (~0u<<12)
    // pre-calculate for benefit of runtime disappearing act via munmap()
    elfout.phdr[0].p_memsz =  PAGE_MASK & (~PAGE_MASK + elfout.phdr[0].p_filesz);
#undef PAGE_MASK
#else  /*}{*/
    elfout.phdr[0].p_memsz =  elfout.phdr[0].p_filesz;
#endif  /*}*/

    // rewrite Elf header
    fo->seek(0, SEEK_SET);
    fo->rewrite(&elfout, overlay_offset);
}

Linker *PackLinuxI386::newLinker() const
{
    return new ElfLinkerX86;
}

void
PackLinuxI386::buildLinuxLoader(
    upx_byte const *const proto,
    unsigned        const szproto,
    upx_byte const *const fold,
    unsigned        const szfold,
    Filter const *ft
)
{
    initLoader(EM_386, proto, szproto);

    unsigned fold_hdrlen = 0;
  if (0 < szfold) {
    cprElfHdr1 const *const hf = (cprElfHdr1 const *)fold;
    fold_hdrlen = usizeof(hf->ehdr) + hf->ehdr.e_phentsize * hf->ehdr.e_phnum +
         usizeof(l_info);
    if (0 == get_le32(fold_hdrlen + fold)) {
        // inconsistent SIZEOF_HEADERS in *.lds (ld, binutils)
        fold_hdrlen = upx::umax(0x80u, fold_hdrlen);
    }
  }
    // This adds the definition to the "library", to be used later.
    // NOTE: the stub is NOT compressed!  The savings is not worth it.
    linker->addSection("FOLDEXEC", fold + fold_hdrlen, szfold - fold_hdrlen, 0);

    n_mru = ft->n_mru;

// Rely on "+80CXXXX" [etc] in getDecompressorSections() packer_c.cpp */
//    // Here is a quick summary of the format of the output file:
//    linker->setLoaderAlignOffset(
//            // Elf32_Edhr
//        sizeof(elfout.ehdr) +
//            // Elf32_Phdr: 1 for exec86, 2 for sh86, 3 for elf86
//        (elfout.ehdr.e_phentsize * elfout.ehdr.e_phnum) +
//            // checksum UPX! lsize version format
//        sizeof(l_info) +
//            // PT_DYNAMIC with DT_NEEDED "forwarded" from original file
//        ((elfout.ehdr.e_phnum==3) ? (unsigned) elfout.phdr[2].p_memsz : 0) +
//            // p_progid, p_filesize, p_blocksize
//        sizeof(p_info) +
//            // compressed data
//        b_len + ph.c_len );
//            // entry to stub
    addLoader("LEXEC000", nullptr);

    if (ft->id) {
        if (n_mru) {
            addLoader("LEXEC009", nullptr);
        }
    }
    addLoader("LEXEC010", nullptr);
    linker->defineSymbol("filter_cto", ft->cto);
    linker->defineSymbol("filter_length",
                         (ft->id & 0xf) % 3 == 0 ? ft->calls :
                         ft->lastcall - ft->calls * 4);
    addLoader(getDecompressorSections(), nullptr);
    addLoader("LEXEC015", nullptr);
    if (ft->id) {
        {  // decompr, unfilter not separate
            if (0x80==(ft->id & 0xF0)) {
                addLoader("LEXEC110", nullptr);
                if (n_mru) {
                    addLoader("LEXEC100", nullptr);
                }
                // bug in APP: jmp and label must be in same .asx/.asy
                addLoader("LEXEC016", nullptr);
            }
        }
        addFilter32(ft->id);
        {  // decompr always unfilters
            addLoader("LEXEC017", nullptr);
        }
    }
    else {
        addLoader("LEXEC017", nullptr);
    }

    addLoader("IDENTSTR", nullptr);
    addLoader("LEXEC020", nullptr);
    addLoader("FOLDEXEC", nullptr);
    if (M_IS_LZMA(ph.method)) {
        const lzma_compress_result_t *res = &ph.compress_result.result_lzma;
        upx_uint32_t properties = // lc, lp, pb, dummy
            (res->lit_context_bits << 0) |
            (res->lit_pos_bits << 8) |
            (res->pos_bits << 16);
        if (bele->isBE()) // big endian - bswap32
            properties = bswap32(properties);
        linker->defineSymbol("lzma_properties", properties);

        // These lengths assume only one block (typ. 524288 bytes: 0.5 MiB).
        // i386 handles more than one block, and computes the lengths
        // dynamically from struct b_info.  Why do others need these?
        if (linker->findSymbol("lzma_c_len", false)) {
            // -2 for properties
            linker->defineSymbol("lzma_c_len", ph.c_len - 2);
        }
        if (linker->findSymbol("lzma_u_len", false)) {
            linker->defineSymbol("lzma_u_len", ph.c_len);
        }

        unsigned const stack = getDecompressorWrkmemSize();
        linker->defineSymbol("lzma_stack_adjust", 0u - stack);
    }
    if (0x80==(ft->id & 0xF0)) {
        int const mru = ft->n_mru ? 1+ ft->n_mru : 0;
        if (mru && mru!=256) {
            unsigned const is_pwr2 = (0==((mru -1) & mru));
            linker->defineSymbol("NMRU", mru - is_pwr2);
        }
    }
    relocateLoader();
}

void
PackLinuxI386::buildLoader(Filter const *ft)
{
    unsigned const sz_fold = sizeof(stub_i386_linux_elf_execve_fold);
    MemBuffer buf(sz_fold);
    memcpy(buf, stub_i386_linux_elf_execve_fold, sz_fold);

    // patch loader
    // note: we only can use /proc/<pid>/fd when exetype > 0.
    //   also, we sleep much longer when compressing a script.
    checkPatch(nullptr, 0, 0, 0);  // reset
    patch_le32(buf,sz_fold,"UPX4",exetype > 0 ? 3 : 15);   // sleep time
    patch_le32(buf,sz_fold,"UPX3",progid);
    patch_le32(buf,sz_fold,"UPX2",exetype > 0 ? 0 : 0x7fffffff);

    buildLinuxLoader(
        stub_i386_linux_elf_execve_entry, sizeof(stub_i386_linux_elf_execve_entry),
        buf, sz_fold, ft );
}

void
PackBSDI386::buildLoader(Filter const *ft)
{
    unsigned const sz_fold = sizeof(stub_i386_bsd_elf_execve_fold);
    MemBuffer buf(sz_fold);
    memcpy(buf, stub_i386_bsd_elf_execve_fold, sz_fold);

    // patch loader
    // note: we only can use /proc/<pid>/fd when exetype > 0.
    //   also, we sleep much longer when compressing a script.
    checkPatch(nullptr, 0, 0, 0);  // reset
    patch_le32(buf,sz_fold,"UPX4",exetype > 0 ? 3 : 15);   // sleep time
    patch_le32(buf,sz_fold,"UPX3",progid);
    patch_le32(buf,sz_fold,"UPX2",exetype > 0 ? 0 : 0x7fffffff);

    buildLinuxLoader(
        stub_i386_bsd_elf_execve_entry, sizeof(stub_i386_bsd_elf_execve_entry),
        buf, sz_fold, ft );
}

// FIXME: getLoaderPrefixSize is unused?
int PackLinuxI386::getLoaderPrefixSize() const
{
    return 116;
}


/*************************************************************************
// some ELF utility functions
**************************************************************************/

// basic check of a Linux ELF Ehdr
int PackLinuxI386::checkEhdr(const Elf_LE32_Ehdr *ehdr) const
{
    const unsigned char * const buf = ehdr->e_ident;

    if (memcmp(buf, "\x7f\x45\x4c\x46\x01\x01\x01", 7)) // ELF 32-bit LSB
        return -1;

    // now check the ELF header
    if (!memcmp(buf+8, "FreeBSD", 7))                  // branded
        return 1;
    if (ehdr->e_type != Elf32_Ehdr::ET_EXEC
    &&  ehdr->e_type != Elf32_Ehdr::ET_DYN )           // executable
        return 2;
    if (ehdr->e_machine != Elf32_Ehdr::EM_386)         // Intel 80386
        return 3;
    if (ehdr->e_version != Elf32_Ehdr::EV_CURRENT)     // version
        return 4;
    if (ehdr->e_phnum < 1)
        return 5;
    if (ehdr->e_phentsize != sizeof(Elf32_Phdr))
        return 6;

    // check for Linux kernels
    if (ehdr->e_entry == 0xC0100000)                    // uncompressed vmlinux
        return 1000;
    if (ehdr->e_entry == 0x00001000)                    // compressed vmlinux
        return 1001;
    if (ehdr->e_entry == 0x00100000)                    // compressed bvmlinux
        return 1002;

    // FIXME: add more checks for kernels

    // FIXME: add special checks for other ELF i386 formats, like
    //        NetBSD, OpenBSD, Solaris, ....

    // success
    return 0;
}



/*************************************************************************
//
**************************************************************************/

tribool PackLinuxI386::canPack()
{
    if (exetype != 0)
        return super::canPack();

    Elf_LE32_Ehdr ehdr;
    unsigned char *buf = ehdr.e_ident;

    fi->seek(0, SEEK_SET);
    fi->readx(&ehdr, sizeof(ehdr));
    fi->seek(0, SEEK_SET);

    exetype = 0;
    const unsigned l = get_le32(buf);

    int elf = checkEhdr(&ehdr);
    if (elf >= 0) {
        // NOTE: ELF executables are now handled by p_lx_elf.cpp,
        //   so we only handle them here if force_execve
        if (elf == 0 && opt->o_unix.force_execve) {
            exetype = 1;

            unsigned osabi0 = ehdr.e_ident[Elf32_Ehdr::EI_OSABI];
            switch (osabi0) {
            case Elf32_Ehdr::ELFOSABI_LINUX:
            case Elf32_Ehdr::ELFOSABI_FREEBSD:
            case Elf32_Ehdr::ELFOSABI_NETBSD:
            case Elf32_Ehdr::ELFOSABI_OPENBSD:
                ei_osabi = osabi0;  // Proudly declares its osabi in Ehdr.
                break;
            default:
            unsigned const e_phnum = get_te16(&ehdr.e_phnum);
            if (e_phnum<=(512/sizeof(Elf32_Phdr))) {
                union {
                    unsigned char buf2[512];
                    //Elf32_Phdr phdr;
                } u;
                fi->seek(get_te32(&ehdr.e_phoff), SEEK_SET);
                fi->readx(u.buf2, sizeof(u.buf2));
                fi->seek(0, SEEK_SET);
                Elf32_Phdr const *phdr = (Elf32_Phdr *) u.buf2;
                for (unsigned j=0; j < e_phnum; ++phdr, ++j) {
                    if (phdr->PT_NOTE == get_te32(&phdr->p_type)) {
                        unsigned const offset = get_te32(&phdr->p_offset);
                        struct Elf32_Note note; memset(&note, 0, sizeof(note));
                        fi->seek(offset, SEEK_SET);
                        fi->readx(&note, sizeof(note));
                        fi->seek(0, SEEK_SET);
                        if (4==get_te32(&note.descsz)
                        &&  1==get_te32(&note.type)
                        &&  0==note.end ) {
                            if (0==strcmp("NetBSD", (char const *)note.text)) {
                                ei_osabi = Elf32_Ehdr::ELFOSABI_NETBSD;
                                break;
                            }
                            if (0==strcmp("OpenBSD", (char const *)note.text)) {
                                ei_osabi = Elf32_Ehdr::ELFOSABI_OPENBSD;
                                break;
                            }
                        }
                    }
                }
            }
            }
        }
        if (UPX_F_BSD_i386==getFormat()
        && !(Elf32_Ehdr::ELFOSABI_FREEBSD==ei_osabi
          || Elf32_Ehdr::ELFOSABI_NETBSD ==ei_osabi
          || Elf32_Ehdr::ELFOSABI_OPENBSD==ei_osabi )) {
            return false;
        }
    }
    else if (l == 0x00640107 || l == 0x00640108 || l == 0x0064010b || l == 0x006400cc)
    {
        // OMAGIC / NMAGIC / ZMAGIC / QMAGIC
        exetype = 2;
        // FIXME: N_TRSIZE, N_DRSIZE
        // FIXME: check for aout shared libraries
    }
    else { // shell scripts and other interpreters
        if (Elf32_Ehdr::ELFOSABI_LINUX!=ei_osabi) {
            return false;  // so far, only Linux has runtime stub for shell
        }
        else if (!memcmp(buf, "#!/", 3))                    // #!/bin/sh
            exetype = -1;
        else if (!memcmp(buf, "#! /", 4))                   // #! /bin/sh
            exetype = -1;
        else if (!memcmp(buf, "\xca\xfe\xba\xbe", 4))       // Java bytecode
            exetype = -2;
    }

    return super::canPack();
}


void PackLinuxI386::patchLoader() { }


void PackLinuxI386::patchLoaderChecksum()
{
    unsigned char *const ptr = getLoader();
    l_info *const lp = (l_info *)(sizeof(elfout.ehdr) +
        (elfout.ehdr.e_phnum * elfout.ehdr.e_phentsize) + (char *)&elfout );
    // checksum for loader + p_info
    lp->l_checksum = 0;
    lp->l_magic = UPX_ELF_MAGIC;
    set_te16(&lp->l_lsize, (upx_uint16_t) lsize);
    lp->l_version = (unsigned char) ph.version;
    lp->l_format  = (unsigned char) ph.format;
    // INFO: lp->l_checksum is currently unused
    set_te32(&lp->l_checksum, upx_adler32(ptr, lsize));
}


void PackLinuxI386::updateLoader(OutputFile *fo)
{
    elfout.ehdr.e_entry = fo->getBytesWritten() + elfout.phdr[0].p_vaddr;
}

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