/* filter_impl.cpp -- filter implementation (low-level)

   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>
 */

#include "../conf.h"
#include "../filter.h"

#define set_dummy(p, v) ((void) 0)
#define get_8(p)        (*(p))
#define set_8(p, v)     (*(p) = (v))

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

#include "getcto.h"

/*************************************************************************
// simple filters: calltrick / swaptrick / delta / ...
**************************************************************************/

#include "ct.h"
#include "sw.h"
#include "ctsw.h"

#include "sub8.h"
#include "sub16.h"
#include "sub32.h"

/*************************************************************************
// cto calltrick
**************************************************************************/

#define COND(b, x) (b[x] == 0xe8)
#define F          f_cto32_e8_bswap_le
#define U          u_cto32_e8_bswap_le
#include "cto.h"
#define F s_cto32_e8_bswap_le
#include "cto.h"
#undef COND

#define COND(b, x) (b[x] == 0xe9)
#define F          f_cto32_e9_bswap_le
#define U          u_cto32_e9_bswap_le
#include "cto.h"
#define F s_cto32_e9_bswap_le
#include "cto.h"
#undef COND

#define COND(b, x) (b[x] == 0xe8 || b[x] == 0xe9)
#define F          f_cto32_e8e9_bswap_le
#define U          u_cto32_e8e9_bswap_le
#include "cto.h"
#define F s_cto32_e8e9_bswap_le
#include "cto.h"
#undef COND

/*************************************************************************
// cto calltrick with jmp
**************************************************************************/

#define COND(b, x, lastcall) (b[x] == 0xe8 || b[x] == 0xe9)
#define F                    f_ctoj32_e8e9_bswap_le
#define U                    u_ctoj32_e8e9_bswap_le
#include "ctoj.h"
#define F s_ctoj32_e8e9_bswap_le
#include "ctoj.h"
#undef COND

/*************************************************************************
// cto calltrick with jmp, optional jcc
**************************************************************************/

#define COND1(b, x)        (b[x] == 0xe8 || b[x] == 0xe9)
#define COND2(b, x, lc)    (lc != (x) && 0xf == b[(x) -1] && 0x80 <= b[x] && b[x] <= 0x8f)
#define COND(b, x, lc, id) (COND1(b, x) || ((9 <= (0xf & (id))) && COND2(b, x, lc)))
#define F                  f_ctok32_e8e9_bswap_le
#define U                  u_ctok32_e8e9_bswap_le
#include "ctok.h"
#define F s_ctok32_e8e9_bswap_le
#include "ctok.h"
#undef COND
#undef COND2
#undef COND1

/*************************************************************************
// cto calltrick with jmp and jcc and relative renumbering
**************************************************************************/

#define COND_CALL(which, b, x) ((which = 0), b[x] == 0xe8)
#define COND_JMP(which, b, x)  ((which = 1), b[x] == 0xe9)
#define COND_JCC(which, b, lastcall, x, y, z)                                                      \
    ((which = 2), (lastcall != (x) && 0xf == b[y] && 0x80 <= b[z] && b[z] <= 0x8f))
#define COND1(which, b, x)                 (COND_CALL(which, b, x) || COND_JMP(which, b, x))
#define COND2(which, b, lastcall, x, y, z) COND_JCC(which, b, lastcall, x, y, z)

#define CONDF(which, b, x, lastcall) (COND1(which, b, x) || COND2(which, b, lastcall, x, (x) -1, x))
#define CONDU(which, b, x, lastcall) (COND1(which, b, x) || COND2(which, b, lastcall, x, x, (x) -1))

#define F f_ctojr32_e8e9_bswap_le
#define U u_ctojr32_e8e9_bswap_le
#include "ctojr.h"
#define F s_ctojr32_e8e9_bswap_le
#include "ctojr.h"

#undef CONDU
#undef CONDF
#undef COND2
#undef COND1
#undef COND_JCC
#undef COND_JMP
#undef COND_CALL

/*************************************************************************
// PowerPC branch [incl. call] trick
**************************************************************************/

#define COND(b, x) (18 == (get_be32(b + x) >> 26))
#define F          f_ppcbxx
#define U          u_ppcbxx
#include "ppcbxx.h"
#define F s_ppcbxx
#include "ppcbxx.h"

#undef COND

/*************************************************************************
// RISC-V AUIPC
**************************************************************************/

#define F f_auipc_le
#define U u_auipc_le
#include "auipc.h"
#undef U
#undef F
#undef opu
#undef func3u
#undef rs1u

#define F s_auipc_le
#include "auipc.h"
#undef F

#undef opf
#undef func3f
#undef rs1f

#undef COND
#undef CONDf
#undef CONDu

#undef COND

/*************************************************************************
// database for use in class Filter
**************************************************************************/

// clang-format off
/*static*/ const FilterImpl::FilterEntry FilterImpl::filters[] = {
    // no filter
    { 0x00, 0,          0, nullptr, nullptr, nullptr },

    // 16-bit calltrick
    { 0x01, 4,          0, f_ct16_e8, u_ct16_e8, s_ct16_e8 },
    { 0x02, 4,          0, f_ct16_e9, u_ct16_e9, s_ct16_e9 },
    { 0x03, 4,          0, f_ct16_e8e9, u_ct16_e8e9, s_ct16_e8e9 },
    { 0x04, 4,          0, f_ct16_e8_bswap_le, u_ct16_e8_bswap_le, s_ct16_e8_bswap_le },
    { 0x05, 4,          0, f_ct16_e9_bswap_le, u_ct16_e9_bswap_le, s_ct16_e9_bswap_le },
    { 0x06, 4,          0, f_ct16_e8e9_bswap_le, u_ct16_e8e9_bswap_le, s_ct16_e8e9_bswap_le },
    { 0x07, 4,          0, f_ct16_e8_bswap_be, u_ct16_e8_bswap_be, s_ct16_e8_bswap_be },
    { 0x08, 4,          0, f_ct16_e9_bswap_be, u_ct16_e9_bswap_be, s_ct16_e9_bswap_be },
    { 0x09, 4,          0, f_ct16_e8e9_bswap_be, u_ct16_e8e9_bswap_be, s_ct16_e8e9_bswap_be },

    // 16-bit swaptrick
    { 0x0a, 4,          0, f_sw16_e8, u_sw16_e8, s_sw16_e8 },
    { 0x0b, 4,          0, f_sw16_e9, u_sw16_e9, s_sw16_e9 },
    { 0x0c, 4,          0, f_sw16_e8e9, u_sw16_e8e9, s_sw16_e8e9 },

    // 16-bit call-/swaptrick
    { 0x0d, 4,          0, f_ctsw16_e8_e9, u_ctsw16_e8_e9, s_ctsw16_e8_e9 },
    { 0x0e, 4,          0, f_ctsw16_e9_e8, u_ctsw16_e9_e8, s_ctsw16_e9_e8 },

    // 32-bit calltrick
    { 0x11, 6,          0, f_ct32_e8, u_ct32_e8, s_ct32_e8 },
    { 0x12, 6,          0, f_ct32_e9, u_ct32_e9, s_ct32_e9 },
    { 0x13, 6,          0, f_ct32_e8e9, u_ct32_e8e9, s_ct32_e8e9 },
    { 0x14, 6,          0, f_ct32_e8_bswap_le, u_ct32_e8_bswap_le, s_ct32_e8_bswap_le },
    { 0x15, 6,          0, f_ct32_e9_bswap_le, u_ct32_e9_bswap_le, s_ct32_e9_bswap_le },
    { 0x16, 6,          0, f_ct32_e8e9_bswap_le, u_ct32_e8e9_bswap_le, s_ct32_e8e9_bswap_le },
    { 0x17, 6,          0, f_ct32_e8_bswap_be, u_ct32_e8_bswap_be, s_ct32_e8_bswap_be },
    { 0x18, 6,          0, f_ct32_e9_bswap_be, u_ct32_e9_bswap_be, s_ct32_e9_bswap_be },
    { 0x19, 6,          0, f_ct32_e8e9_bswap_be, u_ct32_e8e9_bswap_be, s_ct32_e8e9_bswap_be },

    // 32-bit swaptrick
    { 0x1a, 6,          0, f_sw32_e8, u_sw32_e8, s_sw32_e8 },
    { 0x1b, 6,          0, f_sw32_e9, u_sw32_e9, s_sw32_e9 },
    { 0x1c, 6,          0, f_sw32_e8e9, u_sw32_e8e9, s_sw32_e8e9 },

    // 32-bit call-/swaptrick
    { 0x1d, 6,          0, f_ctsw32_e8_e9, u_ctsw32_e8_e9, s_ctsw32_e8_e9 },
    { 0x1e, 6,          0, f_ctsw32_e9_e8, u_ctsw32_e9_e8, s_ctsw32_e9_e8 },

    // 32-bit cto calltrick
    { 0x24, 6, 0x00ffffff, f_cto32_e8_bswap_le, u_cto32_e8_bswap_le, s_cto32_e8_bswap_le },
    { 0x25, 6, 0x00ffffff, f_cto32_e9_bswap_le, u_cto32_e9_bswap_le, s_cto32_e9_bswap_le },
    { 0x26, 6, 0x00ffffff, f_cto32_e8e9_bswap_le, u_cto32_e8e9_bswap_le, s_cto32_e8e9_bswap_le },

    // 32-bit cto calltrick with jmp
    { 0x36, 6, 0x00ffffff, f_ctoj32_e8e9_bswap_le, u_ctoj32_e8e9_bswap_le, s_ctoj32_e8e9_bswap_le },

    // 32-bit calltrick with jmp, optional jcc; runtime can unfilter more than one block
    // Note 0x46 uses 'ctoj', while 0x49 uses 'ctok'.
    { 0x46, 6, 0x00ffffff, f_ctoj32_e8e9_bswap_le, u_ctoj32_e8e9_bswap_le, s_ctoj32_e8e9_bswap_le },
    { 0x49, 6, 0x00ffffff, f_ctok32_e8e9_bswap_le, u_ctok32_e8e9_bswap_le, s_ctok32_e8e9_bswap_le },

    // 24-bit calltrick for arm
    { 0x50, 8, 0x01ffffff, f_ct24arm_le, u_ct24arm_le, s_ct24arm_le },
    { 0x51, 8, 0x01ffffff, f_ct24arm_be, u_ct24arm_be, s_ct24arm_be },

#if 1  //{ old reliable
    // 26-bit calltrick for arm64
    { 0x52, 8, 0x03ffffff, f_ct26arm_le, u_ct26arm_le, s_ct26arm_le },
#else  //}{ new enhanced, but needs new filter id
    // 26-bit calltrick for arm64; also 19-bit and 14-bit
    { 0x52, 8, 0x03ffffff, f_CTarm64_le, u_CTarm64_le, s_CTarm64_le },
#endif  //}

    { 0x55, 8, 0x40000000, f_auipc_le, u_auipc_le, s_auipc_le},
    //{ 0x56, 8, 0x40000000, f_rvjal_le, u_rvjal_le, s_rvjal_le},

    // 32-bit cto calltrick with jmp and jcc(swap 0x0f/0x8Y) and relative renumbering
    { 0x80, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x81, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x82, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x83, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x84, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x85, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x86, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },
    { 0x87, 8, 0x00ffffff, f_ctojr32_e8e9_bswap_le, u_ctojr32_e8e9_bswap_le, s_ctojr32_e8e9_bswap_le },

    // simple delta filter
    { 0x90, 2,          0, f_sub8_1, u_sub8_1, s_sub8_1 },
    { 0x91, 3,          0, f_sub8_2, u_sub8_2, s_sub8_2 },
    { 0x92, 4,          0, f_sub8_3, u_sub8_3, s_sub8_3 },
    { 0x93, 5,          0, f_sub8_4, u_sub8_4, s_sub8_4 },

    { 0xa0,99,          0, f_sub16_1, u_sub16_1, s_sub16_1 },
    { 0xa1,99,          0, f_sub16_2, u_sub16_2, s_sub16_2 },
    { 0xa2,99,          0, f_sub16_3, u_sub16_3, s_sub16_3 },
    { 0xa3,99,          0, f_sub16_4, u_sub16_4, s_sub16_4 },

    { 0xb0,99,          0, f_sub32_1, u_sub32_1, s_sub32_1 },
    { 0xb1,99,          0, f_sub32_2, u_sub32_2, s_sub32_2 },
    { 0xb2,99,          0, f_sub32_3, u_sub32_3, s_sub32_3 },
    { 0xb3,99,          0, f_sub32_4, u_sub32_4, s_sub32_4 },

    // PowerPC branch+call trick
    { 0xd0, 8,          0, f_ppcbxx, u_ppcbxx, s_ppcbxx },
};
// clang-format on

/*static*/ const int FilterImpl::n_filters = TABLESIZE(filters);

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