/* p_wince_arm.cpp --

   This file is part of the UPX executable compressor.

   Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
   Copyright (C) 1996-2024 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>
 */

#include "conf.h"
#include "file.h"
#include "filter.h"
#include "packer.h"
#include "pefile.h"
#include "p_wince_arm.h"
#include "linker.h"

static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/arm.v4a-wince.pe.h"
static const CLANG_FORMAT_DUMMY_STATEMENT
#include "stub/arm.v4t-wince.pe.h"

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

PackWinCeArm::PackWinCeArm(InputFile *f) : super(f) {}

PackWinCeArm::~PackWinCeArm() noexcept {}

Linker *PackWinCeArm::newLinker() const { return new ElfLinkerArmLE; }

const int *PackWinCeArm::getCompressionMethods(int method, int level) const {
    static const int m_all[] = {M_NRV2B_8, M_NRV2E_8, M_LZMA, M_END};
    static const int m_lzma[] = {M_LZMA, M_END};
    static const int m_nrv2b[] = {M_NRV2B_8, M_END};
    static const int m_nrv2e[] = {M_NRV2E_8, M_END};

    if (!use_thumb_stub)
        return getDefaultCompressionMethods_8(method, level);

    if (method == M_ALL)
        return m_all;
    if (M_IS_LZMA(method))
        return m_lzma;
    if (M_IS_NRV2B(method))
        return m_nrv2b;
    if (M_IS_NRV2E(method))
        return m_nrv2e;
    return m_nrv2e;
}

const int *PackWinCeArm::getFilters() const {
    static const int filters[] = {0x50, FT_END};
    return filters;
}

/*************************************************************************
// import handling
**************************************************************************/

void PackWinCeArm::processImports2(unsigned myimport, unsigned iat_off) // pass 2
{
    PeFile::processImports2(myimport, iat_off);

    // adjust import data
    for (import_desc *im = (import_desc *) oimpdlls; im->dllname; im++) {
        im->oft = im->iat;
        bool is_coredll = strcasecmp(kernelDll(), oimpdlls + (im->dllname - myimport)) == 0;
        im->iat = is_coredll ? iat_off : iat_off + 12;
    }
}

void PackWinCeArm::addStubImports() {
    // the order of procedure names below should match the
    // assumptions of the assembly stubs
    // WARNING! these names are sorted alphanumerically by the ImportLinker
    addKernelImport("CacheSync");
    addKernelImport("GetProcAddressA");
    addKernelImport("LoadLibraryW");
}

void PackWinCeArm::processTls(Interval *) // pass 1
{
    if ((sotls = ALIGN_UP(IDSIZE(PEDIR_TLS), 4u)) == 0)
        return;

    // never should happen on wince/arm
    throwCantPack("Static TLS entries found. Send a report please.");
}

/*************************************************************************
// pack
**************************************************************************/

tribool PackWinCeArm::canPack() {
    if (!readFileHeader())
        return false;
    checkMachine(ih.cpu);
    if (ih.cpu != IMAGE_FILE_MACHINE_ARM && ih.cpu != IMAGE_FILE_MACHINE_THUMB)
        return false;
    use_thumb_stub |= ih.cpu == IMAGE_FILE_MACHINE_THUMB || (ih.entry & 1) == 1;
    // HACK FIXME later: don't misuse opt->cpu_x86, need an extra option to force thumb stub
    use_thumb_stub |= (opt->cpu_x86 == opt->CPU_8086);
    return true;
}

void PackWinCeArm::buildLoader(const Filter *ft) {
    const byte *loader = use_thumb_stub ? stub_arm_v4t_wince_pe : stub_arm_v4a_wince_pe;
    unsigned size = use_thumb_stub ? sizeof(stub_arm_v4t_wince_pe) : sizeof(stub_arm_v4a_wince_pe);

    // prepare loader
    initLoader(loader, size);

    if (isdll)
        addLoader("DllStart");
    addLoader("ExeStart");

    if (ph.method == M_NRV2E_8)
        addLoader("Call2E");
    else if (ph.method == M_NRV2B_8)
        addLoader("Call2B");
    else if (ph.method == M_NRV2D_8)
        addLoader("Call2D");
    else if (M_IS_LZMA(ph.method))
        addLoader("+40C,CallLZMA");

    if (ft->id == 0x50)
        addLoader("+40C,Unfilter_0x50");

    if (sorelocs)
        addLoader("+40C,Relocs");

    addLoader("+40C,Imports");
    addLoader("ProcessEnd");

    if (ph.method == M_NRV2E_8)
        addLoader(".ucl_nrv2e_decompress_8");
    else if (ph.method == M_NRV2B_8)
        addLoader(".ucl_nrv2b_decompress_8");
    else if (ph.method == M_NRV2D_8)
        addLoader(".ucl_nrv2d_decompress_8");
    else if (M_IS_LZMA(ph.method))
        addLoader("+40C,LZMA_DECODE,LZMA_DEC10");

    addLoader("IDENTSTR,UPX1HEAD");
}

bool PackWinCeArm::needForceOption() const {
    // return true if we need `--force` to pack this file
    bool r = false;
    r |= (ih.opthdrsize != 0xe0);
    r |= ((ih.flags & IMAGE_FILE_EXECUTABLE_IMAGE) == 0);
    r |= (ih.entry == 0 /*&& !isdll*/);
    r |= (ih.ddirsentries != 16);
    //// r |= (IDSIZE(PEDIR_EXCEPTION) != 0); // is this used on arm?
    //// r |= (IDSIZE(PEDIR_COPYRIGHT) != 0);
    return r;
}

void PackWinCeArm::callCompressWithFilters(Filter &ft, int filter_strategy, unsigned ih_codebase) {
    // limit stack size needed for runtime decompression
    upx_compress_config_t cconf;
    cconf.reset();
    cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
    compressWithFilters(&ft, 2048, &cconf, filter_strategy, ih_codebase, rvamin, 0, nullptr, 0);
}

void PackWinCeArm::addNewRelocations(Reloc &rel, unsigned upxsection) {
    static const char *const symbols_to_relocate[] = {"ONAM", "BIMP", "BREL", "FIBE",
                                                      "FIBS", "ENTR", "DST0", "SRC0"};
    for (unsigned s2r = 0; s2r < TABLESIZE(symbols_to_relocate); s2r++) {
        unsigned off = linker->getSymbolOffset(symbols_to_relocate[s2r]);
        if (off != 0xdeaddead)
            rel.add_reloc(off + upxsection, IMAGE_REL_BASED_HIGHLOW);
    }
}

unsigned PackWinCeArm::getProcessImportParam(unsigned upxsection) {
    return linker->getSymbolOffset("IATT") + upxsection;
}

void PackWinCeArm::defineSymbols(unsigned ncsection, unsigned, unsigned, unsigned ic,
                                 unsigned s1addr) {
    const unsigned onam = ncsection + soxrelocs + ih.imagebase;
    linker->defineSymbol("start_of_dll_names", onam);
    linker->defineSymbol("start_of_imports", ih.imagebase + rvamin + cimports);
    linker->defineSymbol("start_of_relocs", crelocs + rvamin + ih.imagebase);
    linker->defineSymbol("filter_buffer_end", ih.imagebase + ih.codebase + ih.codesize);
    linker->defineSymbol("filter_buffer_start", ih.imagebase + ih.codebase);
    linker->defineSymbol("original_entry", ih.entry + ih.imagebase);
    linker->defineSymbol("uncompressed_length", ph.u_len);
    linker->defineSymbol("start_of_uncompressed", ih.imagebase + rvamin);
    linker->defineSymbol("compressed_length", ph.c_len);
    linker->defineSymbol("start_of_compressed", ih.imagebase + s1addr + ic);
    defineDecompressorSymbols();
}

void PackWinCeArm::setOhDataBase(const pe_section_t *osection) { oh.database = osection[2].vaddr; }

void PackWinCeArm::setOhHeaderSize(const pe_section_t *osection) {
    // SizeOfHeaders
    oh.headersize = osection[1].rawdataptr;
}

void PackWinCeArm::pack(OutputFile *fo) {
    super::pack0(fo, (1u << IMAGE_SUBSYSTEM_WINDOWS_CE_GUI), 0x10000, true);
}

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