/*
 * Copyright (C) 2021 National University of Singapore
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * NOTE: As a special exception, this file is under the MIT license.  The
 *       rest of the E9Patch/E9Tool source code is under the GPLv3 license.
 */

/*
 * This is an example E9Tool plugin.  It implements a limit on control-flow
 * transfer instructions such as calls, jumps, and returns.  When the limit
 * is reached, it will execute the int3 instruction generating a SIGTRAP.
 *
 * To compile:
 *          $ g++ -std=c++11 -fPIC -shared -o example.so -O2 \
 *              examples/plugins/example.cpp -I src/e9tool/
 * 
 * To use:
 *          $ ./e9tool -M 'plugin(example).match()' \
 *                     -P 'plugin(example).patch()' program
 *          $ ./a.out
 *          Trace/breakpoint trap
 */

#include <sstream>
#include <string>

#include <sys/mman.h>
#include <getopt.h>

#include "e9plugin.h"
#include "stdio.h"

using namespace e9tool;

#define COUNTERS        0x789a0000        // Arbitrary

#define OPTION_ADDRESS  1
#define OPTION_LIMIT    2

static intptr_t address = COUNTERS;

/*
 * Initialize the counters and the trampoline.
 */
extern void *e9_plugin_init(const Context *cxt)
{
    printf("[*] init...\n");

    char *function_arr[]={"malloc"}
    intptr_t target_addr[10];
    bool end = false;
    intptr_t val = getELFObject(cxt->elf, symbol, end);

    if (val == -1)
    {
        warning("symbol \"%s\" is undefined and therefore has value 0x0",symbol);
    }
    else if (val == INTPTR_MIN)
    {
        warning("failed to match \"%s\" against any section or symbol name",symbol);
    }
    // construct target addr list

    return nullptr;
}

/*
 * We match all control-flow transfer instructions.
 */
extern intptr_t e9_plugin_match(const Context *cxt)
{
    printf("[*] matching..\n");
    intptr_t intr_addr=0;
    // The e9_plugin_match() function is invoked once by E9Tool for each
    // disassembled instruction.  The function should return a value that is
    // used for matching.

    // For this example we return a non-zero value for all
    // control-flow-transfer instructions:
    switch (cxt->I->mnemonic)
    {
        // append
        case MNEMONIC_RET:
            return 1;
        case MNEMONIC_JB: case MNEMONIC_JBE: case MNEMONIC_JCXZ:
        case MNEMONIC_JECXZ: case MNEMONIC_JKNZD: case MNEMONIC_JKZD:
        case MNEMONIC_JL: case MNEMONIC_JLE: case MNEMONIC_JMP:
        case MNEMONIC_JNB: case MNEMONIC_JNBE: case MNEMONIC_JNL:
        case MNEMONIC_JNLE: case MNEMONIC_JNO: case MNEMONIC_JNP:
        case MNEMONIC_JNS: case MNEMONIC_JNZ: case MNEMONIC_JO:
        case MNEMONIC_JP: case MNEMONIC_JRCXZ: case MNEMONIC_JS:
        case MNEMONIC_JZ:
            return 2;
        case MNEMONIC_CALL:
            // if (cxt->I->count.op != 1 || cxt->I->op[0].type != OPTYPE_IMM){
            //     ; // undefine address
            // }
            // else{
            //     intr_addr=(intptr_t)cxt->I->op[0].imm + (intptr_t)cxt->I->address +(intptr_t)cxt->I->size;
            //     // if addr in target list
            // }   
            return 3;
        default:
            return 0;
    }
}

/*
 * Emit the patch template code.
 */
extern void e9_plugin_code(const Context *cxt)
{
    // The e9_plugin_code() function is invoked once by E9tool.
    // The function specifies the "code" part of the trampoline template that
    // will be executed for each matching instruction.

    // In this example, the code simply invokes the $limit template
    // (defined above):
    fputs("\"$limit\",", cxt->out);
}

/*
 * Emit the patch template data.
 */
extern void e9_plugin_data(const Context *cxt)
{
    // The e9_plugin_code() function is invoked once by E9tool.
    // The function specifies the "data" part of the trampoline template that
    // will be attached to each matching instruction.

    // In this example, there is no data so this function does nothing.
    // The function could also be removed from the plugin.
}

/*
 * Patch the selected instructions.
 */
extern void e9_plugin_patch(const Context *cxt)
{
    printf("[*] patching..\n");
    // The e9_plugin_patch() function is invoked by E9Tool once per
    // matching instruction.  The function specifies the "metadata" which
    // instantiates any macros in the trampoline template (both code or data).
    // The metadata is specified in as comma-separated "$key":VALUE pairs,
    // where $key is a macro name and VALUE is a value in the template
    // template format.

    // In this example, the metadata instantiates the $counter macro with
    // the counter address corresponding to the instruction type:
    intptr_t counter = e9_plugin_match(cxt);
}