/* Chrysalide - Outil d'analyse de fichiers binaires
 * symbol.c - gestion des symboles dans un binaire
 *
 * Copyright (C) 2009-2012 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 <http://www.gnu.org/licenses/>.
 */


#include "symbol.h"


#include <assert.h>
#include <string.h>



/* Symbole d'exécutable (instance) */
struct _GBinSymbol
{
    GObject parent;                         /* A laisser en premier        */

    SymbolType type;                        /* Type du symbole             */

    char *alt;                              /* Nom alternatif              */

    union
    {
        GArchInstruction *instr;            /* Instruction correspondante  */
        GBinRoutine *routine;               /* Compléments pour fonction   */

    } extra;

    GDbComment *comment;                    /* Eventuel commentaire lié    */

};

/* Symbole d'exécutable (classe) */
struct _GBinSymbolClass
{
    GObjectClass parent;                    /* A laisser en premier        */

};


/* Initialise la classe des symboles d'exécutables. */
static void g_binary_symbol_class_init(GBinSymbolClass *);

/* Initialise une instance de symbole d'exécutable. */
static void g_binary_symbol_init(GBinSymbol *);

/* Supprime toutes les références externes. */
static void g_binary_symbol_dispose(GBinSymbol *);

/* Procède à la libération totale de la mémoire. */
static void g_binary_symbol_finalize(GBinSymbol *);



/* Indique le type défini pour un symbole d'exécutable. */
G_DEFINE_TYPE(GBinSymbol, g_binary_symbol, G_TYPE_OBJECT);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des symboles d'exécutables.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_class_init(GBinSymbolClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_symbol_dispose;
    object->finalize = (GObjectFinalizeFunc)g_binary_symbol_finalize;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une instance de symbole d'exécutable.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_init(GBinSymbol *symbol)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_dispose(GBinSymbol *symbol)
{
    /* TODO... */

    G_OBJECT_CLASS(g_binary_symbol_parent_class)->dispose(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_finalize(GBinSymbol *symbol)
{
    free(symbol->alt);

    G_OBJECT_CLASS(g_binary_symbol_parent_class)->finalize(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : type = type de symbole à créer.                              *
*                                                                             *
*  Description : Crée un nouveau symbole d'exécutable.                        *
*                                                                             *
*  Retour      : Adresse de l'instance mise en place ou NULL en cas d'échec.  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinSymbol *g_binary_symbol_new(SymbolType type)
{
    GBinSymbol *result;                     /* Nouveau symbole à renvoyer  */

    result = g_object_new(G_TYPE_BIN_SYMBOL, NULL);

    result->type = type;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : a = premier symbole à analyser.                              *
*                b = second symbole à analyser.                               *
*                                                                             *
*  Description : Compare deux symboles d'exécutable selon leurs propriétés.   *
*                                                                             *
*  Retour      : Bilan de la comparaison : -1, 0 ou 1 (-1 par défaut).        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int g_binary_symbol_cmp(const GBinSymbol **a, const GBinSymbol **b)
{
    int result;                             /* Bilan à retourner           */
	const mrange_t *ra;						/* Emplacement du symbole A	   */
	const mrange_t *rb;						/* Emplacement du symbole B	   */
    const vmpa2t *aa;                       /* Adresse du symbole A        */
    const vmpa2t *ab;                       /* Adresse du symbole B        */

	ra = g_binary_symbol_get_range(*a);
	rb = g_binary_symbol_get_range(*b);

	if (ra == NULL && rb == NULL)
		result = 0;

	else if (ra != NULL && rb == NULL)
		result = 1;

	else if (ra == NULL && rb != NULL)
		result = -1;

	else
	{
		aa = get_mrange_addr(ra);
		ab = get_mrange_addr(rb);

        result = cmp_vmpa_by_virt(aa, ab);

		//result = aa->virtual < ab->virtual ? -1 : (aa->virtual > ab->virtual ? 1 : 0);
		///result = cmp_mrange(ra, rb);

		//printf(" ?? 0x%08lx vs 0x%08lx -> %d\n", aa->virtual, ab->virtual, result);

	}

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit le type du symbole.                                  *
*                                                                             *
*  Retour      : Type de symbole représenté.                                  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

SymbolType g_binary_symbol_get_target_type(const GBinSymbol *symbol)
{
    return symbol->type;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit un étiquette pour viser un symbole.                  *
*                                                                             *
*  Retour      : Chaîne de caractères renvoyant au symbole.                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const char *g_binary_symbol_get_label(const GBinSymbol *symbol)
{
    const char *result;                     /* Etiquette à retourner       */

    if (symbol->alt != NULL)
        return symbol->alt;

    result = NULL;

    switch (symbol->type)
    {
        case STP_ROUTINE:
        case STP_ENTRY_POINT:
        case STP_CODE_LABEL:
            result = g_binary_routine_get_name(symbol->extra.routine);
            break;

        default:
            result = NULL;
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                alt    = désignation humaine alternative à favoriser.        *
*                                                                             *
*  Description : Définit un autre nom pour le symbole.                        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_alt_label(GBinSymbol *symbol, const char *alt)
{
    if (symbol->alt != NULL)
        free(symbol->alt);

    if (alt == NULL)
        symbol->alt = NULL;
    else
        symbol->alt = strdup(alt);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir mettre à jour.                      *
*                full   = adresse dont la définition est complète.            *
*                                                                             *
*  Description : Raffine la définition de l'emplacement d'un symbole.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_fix_range(GBinSymbol *symbol, const vmpa2t *full)
{
    GArchInstruction *instr;                /* Instruction associée        */
    mrange_t range;                         /* Plage à manipuler           */
    GBinRoutine *routine;                   /* Routine associée            */

    switch (symbol->type)
    {
        case STP_DATA:
        case STP_RO_STRING:

            instr = g_binary_symbol_get_instruction(symbol);

            copy_mrange(&range, g_arch_instruction_get_range(instr));

            assert(cmp_vmpa(get_mrange_addr(&range), full) == 0);

            copy_vmpa(get_mrange_addr(&range), full);

            g_arch_instruction_set_range(instr, &range);

            break;

        case STP_ROUTINE:
        case STP_ENTRY_POINT:
        case STP_CODE_LABEL:

            routine = g_binary_symbol_get_routine(symbol);

            copy_mrange(&range, g_binary_routine_get_range(routine));

            assert(cmp_vmpa(get_mrange_addr(&range), full) == 0);

            copy_vmpa(get_mrange_addr(&range), full);

            g_binary_routine_set_range(routine, &range);

            break;

        default:
            break;

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit l'emplacement où se situe un symbole.                *
*                                                                             *
*  Retour      : Zone mémoire couverte par le symbole.                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const mrange_t *g_binary_symbol_get_range(const GBinSymbol *symbol)
{
    const mrange_t *result;                 /* Plage à retourner           */

    result = NULL;

    switch (symbol->type)
    {
        case STP_DATA:
        case STP_RO_STRING:
            result = g_arch_instruction_get_range(symbol->extra.instr);
            break;

        case STP_ROUTINE:
        case STP_ENTRY_POINT:
        case STP_CODE_LABEL:
            result = g_binary_routine_get_range(symbol->extra.routine);
            break;

        default:
            /* FIXME : assert(0); */
            result = NULL;
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol  = symbole à venir consulter.                         *
*                routine = prototype de la fonction représentée.              *
*                type    = (nouveau) type du symbole attaché.                 *
*                                                                             *
*  Description : Attache la routine associée au symbole.                      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void _g_binary_symbol_attach_routine(GBinSymbol *symbol, GBinRoutine *routine, SymbolType type)
{
    if (symbol->extra.routine != NULL)
        g_object_unref(G_OBJECT(symbol->extra.routine));

    symbol->type = type;

    symbol->extra.routine = routine;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol  = symbole à venir consulter.                         *
*                routine = prototype de la fonction représentée.              *
*                                                                             *
*  Description : Attache la routine associée au symbole.                      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_attach_routine(GBinSymbol *symbol, GBinRoutine *routine)
{
    _g_binary_symbol_attach_routine(symbol, routine, symbol->type);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir manipuler.                          *
*                instr  = représentation du symbole associé.                  *
*                                                                             *
*  Description : Attache l'instruction associée au symbole.                   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_attach_instruction(GBinSymbol *symbol, GArchInstruction *instr)
{
    if (symbol->type != STP_RO_STRING)
    symbol->type = STP_DATA;

    symbol->extra.instr = instr;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit l'éventuelle routine associée au symbole.            *
*                                                                             *
*  Retour      : Instance GLib en place ou NULL si aucune.                    *
*                                                                             *
*  Remarques   : Il n'y a pas de transfert de propriété ici !                 *
*                                                                             *
******************************************************************************/

GBinRoutine *g_binary_symbol_get_routine(const GBinSymbol *symbol)
{
    return symbol->extra.routine;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit l'éventuelle instruction associée au symbole.        *
*                                                                             *
*  Retour      : Instance GLib en place ou NULL si aucune.                    *
*                                                                             *
*  Remarques   : Il n'y a pas de transfert de propriété ici !                 *
*                                                                             *
******************************************************************************/

GArchInstruction *g_binary_symbol_get_instruction(const GBinSymbol *symbol)
{
    return symbol->extra.instr;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol  = symbole à venir manipuler.                         *
*                comment = commentaire construit à propos du symbole.         *
*                                                                             *
*  Description : Ajoute un commentaire facultatif au symbole.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_comment(GBinSymbol *symbol, GDbComment *comment)
{
    symbol->comment = comment;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit l'éventuel commentaire associé au symbole.           *
*                                                                             *
*  Retour      : Instance GLib en place ou NULL si aucune.                    *
*                                                                             *
*  Remarques   : Il n'y a pas de transfert de propriété ici !                 *
*                                                                             *
******************************************************************************/

GDbComment *g_binary_symbol_get_comment(const GBinSymbol *symbol)
{
    return symbol->comment;

}