/* Chrysalide - Outil d'analyse de fichiers binaires
 * context.c - contexte lié à l'exécution d'un processeur
 *
 * Copyright (C) 2011-2013 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  OpenIDA is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenIDA 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 Foobar.  If not, see .
 */
#include "context.h"
#include 
#include 
#include 
#include "operands/register.h"
#include "../context-int.h"
#include "../raw.h"
#include "../../analysis/contents/restricted.h"
#include "../../common/sort.h"
#include "../../decomp/context-int.h"
#include "../../decomp/expr/pseudo.h"
#include "../../format/dex/dex-int.h"
/* ------------------------ MANIPULATION GLOBALE DU CONTEXTE ------------------------ */
/* Mémorisation de données brutes dans le code */
typedef struct _raw_data_area
{
    mrange_t range;                         /* Couverture à laisser en 1er */
    phys_t item_len;                        /* Taille de chaque élément    */
} raw_data_area;
/* Définition d'un contexte pour processeur Dalkvik (instance) */
struct _GDalvikContext
{
    GProcContext parent;                    /* A laisser en premier        */
    raw_data_area *data;                    /* Liste de zones brutes       */
    size_t count;                           /* Taille de cette liste       */
    GMutex mutex;                           /* Accès à la liste            */
};
/* Définition d'un contexte pour processeur Dalkvik (classe) */
struct _GDalvikContextClass
{
    GProcContextClass parent;               /* A laisser en premier        */
};
/* Initialise la classe des contextes de processeur Dalkvik. */
static void g_dalvik_context_class_init(GDalvikContextClass *);
/* Initialise une instance de contexte de processeur Dalkvik. */
static void g_dalvik_context_init(GDalvikContext *);
/* Supprime toutes les références externes. */
static void g_dalvik_context_dispose(GDalvikContext *);
/* Procède à la libération totale de la mémoire. */
static void g_dalvik_context_finalize(GDalvikContext *);
/* ------------------------- CONTEXTE POUR LA DECOMPILATION ------------------------- */
/* Définition d'un contexte pour décompilation Dalkvik (instance) */
struct _GDalvikDContext
{
    GDecContext parent;                     /* A laisser en premier        */
    GDecInstruction *this;                  /* Représentation de la classe */
    GHashTable *args;                       /* Correspondance arg./pseudo  */
    GHashTable *locals;                     /* Correspondance var./pseudo  */
    size_t locals_count;                    /* Quantité de var. locales    */
};
/* Définition d'un contexte pour décompilation Dalkvik (classe) */
struct _GDalvikDContextClass
{
    GDecContextClass parent;                /* A laisser en premier        */
};
/* Initialise la classe des contextes de décompilation Dalkvik. */
static void g_dalvik_dcontext_class_init(GDalvikDContextClass *);
/* Initialise une instance de contexte de décompilation Dalkvik. */
static void g_dalvik_dcontext_init(GDalvikDContext *);
/* Supprime toutes les références externes. */
static void g_dalvik_dcontext_dispose(GDalvikDContext *);
/* Procède à la libération totale de la mémoire. */
static void g_dalvik_dcontext_finalize(GDalvikDContext *);
/* Duplique un contexte de compilation. */
static GDalvikDContext *g_dalvik_dcontext_dup(GDalvikDContext *);
/* Propage un registre alloué et attendu par la suite. */
static void g_dalvik_context_spread_allocated_shared_reg(GDalvikDContext *, GDalvikRegister *, GDecInstruction *);
/* Convertit un registre machine en un pseudo-registre. */
static GDecInstruction *g_dalvik_dcontext_convert_register(GDalvikDContext *, GDalvikRegisterOperand *, bool, vmpa_t);
/* ---------------------------------------------------------------------------------- */
/*                          MANIPULATION GLOBALE DU CONTEXTE                          */
/* ---------------------------------------------------------------------------------- */
/* Indique le type définit par la GLib pour le contexte de processeur Dalkvik. */
G_DEFINE_TYPE(GDalvikContext, g_dalvik_context, G_TYPE_PROC_CONTEXT);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des contextes de processeur Dalkvik.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_context_class_init(GDalvikContextClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    object = G_OBJECT_CLASS(klass);
    object->dispose = (GObjectFinalizeFunc/* ! */)g_dalvik_context_dispose;
    object->finalize = (GObjectFinalizeFunc)g_dalvik_context_finalize;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance à initialiser.                                *
*                                                                             *
*  Description : Initialise une instance de contexte de processeur Dalkvik.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_context_init(GDalvikContext *ctx)
{
    g_mutex_init(&ctx->mutex);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance d'objet GLib à traiter.                       *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_context_dispose(GDalvikContext *ctx)
{
    g_mutex_clear(&ctx->mutex);
    G_OBJECT_CLASS(g_dalvik_context_parent_class)->dispose(G_OBJECT(ctx));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance d'objet GLib à traiter.                       *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_context_finalize(GDalvikContext *ctx)
{
    if (ctx->data != NULL)
        free(ctx->data);
    G_OBJECT_CLASS(g_dalvik_context_parent_class)->finalize(G_OBJECT(ctx));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un contexte pour l'exécution du processeur Dalvik.      *
*                                                                             *
*  Retour      : Contexte mis en place.                                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GDalvikContext *g_dalvik_context_new(void)
{
    GDalvikContext *result;                 /* Structure à retourner       */
    result = g_object_new(G_TYPE_DALVIK_CONTEXT, NULL);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx    = contexte de désassemblage Dalvik à actualiser.      *
*                start  = début de la zone à considérer.                      *
*                length = taille de la zone couverte.                         *
*                                                                             *
*  Description : Mémorise une zone comme étant des données de branchements.   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_dalvik_context_register_switch_data(GDalvikContext *ctx, const vmpa2t *start, phys_t length)
{
    bool result;                            /* Bilan à retourner           */
    raw_data_area new;                      /* Nouvel élément à insérer    */
    size_t i;                               /* Boucle de parcours          */
    result = true;
    g_mutex_lock(&ctx->mutex);
    /* Vérification quant aux chevauchements */
    init_mrange(&new.range, start, length);
    for (i = 0; i < ctx->count && result; i++)
        result = !mrange_intersects_mrange(&ctx->data[i].range, &new.range);
    /* Insertion d'une nouvelle zone */
    if (result)
    {
        new.item_len = 4;
        ctx->data = qinsert(ctx->data, &ctx->count, sizeof(raw_data_area),
                            (__compar_fn_t)cmp_mrange_with_vmpa_swapped, &new);
    }
    g_mutex_unlock(&ctx->mutex);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx    = contexte de désassemblage Dalvik à actualiser.      *
*                start  = début de la zone à considérer.                      *
*                width  = taille de chacun des éléments.                      *
*                length = taille de la zone couverte.                         *
*                                                                             *
*  Description : Mémorise une zone comme étant des données d'un tableau.      *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_dalvik_context_register_array_data(GDalvikContext *ctx, const vmpa2t *start, uint16_t width, phys_t length)
{
    bool result;                            /* Bilan à retourner           */
    raw_data_area new;                      /* Nouvel élément à insérer    */
    size_t i;                               /* Boucle de parcours          */
    result = true;
    g_mutex_lock(&ctx->mutex);
    /* Vérification quant aux chevauchements */
    init_mrange(&new.range, start, length);
    for (i = 0; i < ctx->count && result; i++)
        result = !mrange_intersects_mrange(&ctx->data[i].range, &new.range);
    /* Insertion d'une nouvelle zone */
    if (result)
    {
        new.item_len = width;
        ctx->data = qinsert(ctx->data, &ctx->count, sizeof(raw_data_area),
                            (__compar_fn_t)cmp_mrange_with_vmpa_swapped, &new);
    }
    g_mutex_unlock(&ctx->mutex);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx     = contexte de désassemblage Dalvik à consulter.      *
*                content = flux de données à analyser.                        *
*                pos     = position courante dans ce flux. [OUT]              *
*                                                                             *
*  Description : Place une donnée en tant qu'instruction si besoin est.       *
*                                                                             *
*  Retour      : Instruction mise en place ou NULL en cas d'échec.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchInstruction *g_dalvik_context_get_raw_data(GDalvikContext *ctx, const GBinContent *content, vmpa2t *pos)
{
    GArchInstruction *result;               /* Instruction à retourner     */
    raw_data_area *found;                   /* Zone de couverture trouvée  */
    GBinContent *restricted;                /* Zone de lecture effective   */
    result = NULL;
    g_mutex_lock(&ctx->mutex);
    found = bsearch(pos, ctx->data, ctx->count, sizeof(raw_data_area),
                    (__compar_fn_t)cmp_mrange_with_vmpa_swapped);
    if (found)
    {
        restricted = g_restricted_content_new_ro(content, &found->range);
        switch (found->item_len)
        {
            case 1:
                result = g_raw_instruction_new_array(restricted, MDS_8_BITS_UNSIGNED, 1, pos, SRE_LITTLE);
                break;
            case 2:
                result = g_raw_instruction_new_array(restricted, MDS_16_BITS_UNSIGNED, 1, pos, SRE_LITTLE);
                break;
            case 4:
                result = g_raw_instruction_new_array(restricted, MDS_32_BITS_UNSIGNED, 1, pos, SRE_LITTLE);
                break;
            case 8:
                result = g_raw_instruction_new_array(restricted, MDS_64_BITS_UNSIGNED, 1, pos, SRE_LITTLE);
                break;
            default:
                result = g_raw_instruction_new_array(restricted, MDS_8_BITS_UNSIGNED,
                                                     found->item_len, pos, SRE_LITTLE);
                break;
        }
        g_object_unref(G_OBJECT(restricted));
    }
    g_mutex_unlock(&ctx->mutex);
    return result;
}
/* ---------------------------------------------------------------------------------- */
/*                           CONTEXTE POUR LA DECOMPILATION                           */
/* ---------------------------------------------------------------------------------- */
/* Indique le type définit par la GLib pour le contexte de décompilation Dalkvik. */
G_DEFINE_TYPE(GDalvikDContext, g_dalvik_dcontext, G_TYPE_DEC_CONTEXT);
/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des contextes de décompilation Dalkvik. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_dcontext_class_init(GDalvikDContextClass *class)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    object = G_OBJECT_CLASS(class);
    object->dispose = (GObjectFinalizeFunc/* ! */)g_dalvik_dcontext_dispose;
    object->finalize = (GObjectFinalizeFunc)g_dalvik_dcontext_finalize;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance à initialiser.                                *
*                                                                             *
*  Description : Initialise une instance de contexte de décompilation Dalkvik.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_dcontext_init(GDalvikDContext *ctx)
{
    GDecContext *parent;                    /* Instance parente            */
    ctx->args = g_hash_table_new(g_constant_hash, g_direct_equal);
    ctx->locals = g_hash_table_new(g_constant_hash, g_direct_equal);
    parent = G_DEC_CONTEXT(ctx);
    parent->dup = (dup_dec_context_fc)g_dalvik_dcontext_dup;
    parent->spread = (spread_reg_fc)g_dalvik_context_spread_allocated_shared_reg;
    parent->convert_reg = (convert_register_fc)g_dalvik_dcontext_convert_register;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance d'objet GLib à traiter.                       *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_dcontext_dispose(GDalvikDContext *ctx)
{
    if (ctx->this != NULL)
        g_object_unref(G_OBJECT(ctx->this));
    G_OBJECT_CLASS(g_dalvik_dcontext_parent_class)->dispose(G_OBJECT(ctx));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx = instance d'objet GLib à traiter.                       *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_dcontext_finalize(GDalvikDContext *ctx)
{
    G_OBJECT_CLASS(g_dalvik_dcontext_parent_class)->finalize(G_OBJECT(ctx));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un contexte pour la décompilation Dalvik.               *
*                                                                             *
*  Retour      : Contexte mis en place.                                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GDalvikDContext *g_dalvik_dcontext_new(void)
{
    GDalvikDContext *result;                /* Structure à retourner       */
    result = g_object_new(G_TYPE_DALVIK_DCONTEXT, NULL);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : orig = contexte de compilation à copier.                     *
*                                                                             *
*  Description : Duplique un contexte de compilation.                         *
*                                                                             *
*  Retour      : Contexte de décompilation prêt à emploi.                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GDalvikDContext *g_dalvik_dcontext_dup(GDalvikDContext *orig)
{
    GDalvikDContext *result;                /* Structure à retourner       */
    result = g_object_new(G_TYPE_DALVIK_DCONTEXT, NULL);
    //g_object_unref(G_OBJECT(result->args));
    //g_object_unref(G_OBJECT(result->locals));
    _g_dec_context_dup(G_DEC_CONTEXT(result), G_DEC_CONTEXT(orig));
    if (orig->this != NULL) g_object_ref(G_OBJECT(orig->this));
    //g_object_ref(G_OBJECT(orig->args));
    //g_object_ref(G_OBJECT(orig->locals));
    result->this = orig->this;
    result->args = orig->args;
    result->locals = orig->locals;
    result->locals_count = orig->locals_count;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : parent = instance à éventuellement compléter.                *
*                child  = instance à venir consulter.                         *
*                                                                             *
*  Description : Propage un registre alloué et attendu par la suite.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_dalvik_context_spread_allocated_shared_reg(GDalvikDContext *ctx, GDalvikRegister *reg, GDecInstruction *dinstr)
{
    GDexFormat *format;                     /* Recherche de méthode        */
    GBinRoutine *routine;                   /* Objet des recherches        */
    GDexMethod *method;                     /* Méthode décompilée          */
    uint16_t index;                         /* Identifiant du registre     */
    DexVariableIndex info;                  /* Nature du registre          */
    format = G_DEX_FORMAT(G_DEC_CONTEXT(ctx)->format);
    routine = G_DEC_CONTEXT(ctx)->routine;
    method = g_dex_format_find_method_by_address(format, g_binary_routine_get_address(routine));
    index = g_dalvik_register_get_index(reg);
    info = g_dex_method_get_variable(method, index);
    g_object_ref(G_OBJECT(dinstr));
    g_hash_table_insert(ctx->locals, GUINT_TO_POINTER(DVI_INDEX(info)), dinstr);
    ctx->locals_count++;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : ctx     = instance à consulter, voire mettre à jour.         *
*                operand = opérande représentant un registre quelconque.      *
*                assign  = précise le sort prochain du registre.              *
*                addr    = adresse de l'instruction décompilée.               *
*                                                                             *
*  Description : Convertit un registre machine en un pseudo-registre.         *
*                                                                             *
*  Retour      : Pseudo-registre, existant ou non, prêt à emploi.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GDecInstruction *g_dalvik_dcontext_convert_register(GDalvikDContext *ctx, GDalvikRegisterOperand *operand, bool assign, vmpa_t addr)
{
    GDecInstruction *result;                /* Instance à retourner        */
    GDexFormat *format;                     /* Recherche de méthode        */
    GBinRoutine *routine;                   /* Objet des recherches        */
    GDexMethod *method;                     /* Méthode décompilée          */
    GDalvikRegister *reg;                   /* Registre Dalvik représenté  */
    uint16_t index;                         /* Identifiant du registre     */
    DexVariableIndex info;                  /* Nature du registre          */
    GBinVariable *this;                     /* Définition de "this"        */
    gpointer *found;                        /* Pseudo-registre trouvé      */
    format = G_DEX_FORMAT(G_DEC_CONTEXT(ctx)->format);
    routine = G_DEC_CONTEXT(ctx)->routine;
    method = g_dex_format_find_method_by_address(format, g_binary_routine_get_address(routine));
    reg = g_dalvik_register_operand_get(operand);
    index = g_dalvik_register_get_index(reg);
    info = g_dex_method_get_variable(method, index);
    /* Objet "this" */
    if (info & DVI_THIS)
    {
        if (ctx->this == NULL)
        {
            this = g_binary_variable_new(/* FIXME */g_basic_type_new(BTP_OTHER) /* FIXME */);
            g_binary_variable_set_name(this, "this");
            ctx->this = g_pseudo_register_new(PRU_THIS);
            g_pseudo_register_set_variable(G_PSEUDO_REGISTER(ctx->this), this);
        }
        g_object_ref(G_OBJECT(ctx->this));
        result = ctx->this;
    }
    /* Argument d'appel */
    else if (info & DVI_ARGUMENT)
    {
        found = g_hash_table_lookup(ctx->args, GUINT_TO_POINTER(DVI_INDEX(info)));
        if (found != NULL)
        {
            g_object_ref(G_OBJECT(found));
            result = G_DEC_INSTRUCTION(found);
        }
        else
        {
            result = g_pseudo_register_new(PRU_ARG);
            g_pseudo_register_set_basename(G_PSEUDO_REGISTER(result), "arg");
            g_pseudo_register_set_index(G_PSEUDO_REGISTER(result), DVI_INDEX(info));
            g_hash_table_insert(ctx->args, GUINT_TO_POINTER(DVI_INDEX(info)), result);
        }
    }
    /* Variable locale */
    else
    {
        found = g_hash_table_lookup(ctx->locals, GUINT_TO_POINTER(DVI_INDEX(info)));
        if (!assign && found != NULL)
        {
            g_object_ref(G_OBJECT(found));
            result = G_DEC_INSTRUCTION(found);
        }
        else
        {
            /*
            if (!assign)
            {
                printf("bug");
                exit(0);
            }
            */
            result = g_dec_context_get_awaited_alloc(G_DEC_CONTEXT(ctx), G_ARCH_REGISTER(reg), addr);
            if (result == NULL)
            {
                result = g_pseudo_register_new(PRU_LOCAL);
                g_pseudo_register_set_basename(G_PSEUDO_REGISTER(result), "var");
                g_pseudo_register_set_index(G_PSEUDO_REGISTER(result), ctx->locals_count);
                g_dec_context_notify_reg_alloc(G_DEC_CONTEXT(ctx), G_ARCH_REGISTER(reg),
                                               result, addr);
            }
            else
                g_object_ref(G_OBJECT(result));
            g_object_ref(G_OBJECT(result));
            g_hash_table_insert(ctx->locals, GUINT_TO_POINTER(DVI_INDEX(info)), result);
            ctx->locals_count++;
        }
    }
    return result;
}