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

   This file is part of the UPX executable compressor.

   Copyright (C) 1996-2018 Markus Franz Xaver Johannes Oberhumer
   Copyright (C) 1996-2018 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"

static unsigned
umin(unsigned const a, unsigned const b)
{
    return (a<=b) ? a : b;
}


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


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

#include "filter/getcto.h"


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

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

#include "filter/sub8.h"
#include "filter/sub16.h"
#include "filter/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 "filter/cto.h"
#define F                       s_cto32_e8_bswap_le
#include "filter/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 "filter/cto.h"
#define F                       s_cto32_e9_bswap_le
#include "filter/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 "filter/cto.h"
#define F                       s_cto32_e8e9_bswap_le
#include "filter/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 "filter/ctoj.h"
#define F                       s_ctoj32_e8e9_bswap_le
#include "filter/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 "filter/ctok.h"
#define F                       s_ctok32_e8e9_bswap_le
#include "filter/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 "filter/ctojr.h"
#define F                       s_ctojr32_e8e9_bswap_le
#include "filter/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 "filter/ppcbxx.h"
#define F                       s_ppcbxx
#include "filter/ppcbxx.h"
#undef COND


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

const FilterImp::FilterEntry FilterImp::filters[] = {
    // no filter
    { 0x00, 0,          0, NULL, NULL, NULL },

    // 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
    { 0x46, 6, 0x00ffffff, f_ctok32_e8e9_bswap_le, u_ctok32_e8e9_bswap_le, s_ctok32_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 },

    // 26-bit calltrick for arm64
    { 0x52, 8, 0x03ffffff, f_ct26arm_le, u_ct26arm_le, s_ct26arm_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 },
};

const int FilterImp::n_filters = TABLESIZE(filters);

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