/* Chrysalide - Outil d'analyse de fichiers binaires
 * executable.c - support des formats d'exécutables
 *
 * Copyright (C) 2009-2019 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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.
 *
 *  Chrysalide 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 Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "executable.h"


#include <assert.h>
#include <malloc.h>
#include <stdio.h>


#include <i18n.h>


#include "executable-int.h"
#include "../core/logs.h"



/* ------------------------- GESTION D'UN FORMAT EXECUTABLE ------------------------- */


/* Initialise la classe des formats d'exécutables génériques. */
static void g_executable_format_class_init(GExecutableFormatClass *);

/* Initialise une instance de format d'exécutable générique. */
static void g_executable_format_init(GExecutableFormat *);

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

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



/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */


/* Assure l'interprétation d'un format en différé. */
static bool g_executable_format_analyze(GExecutableFormat *);



/* ---------------------------------------------------------------------------------- */
/*                           GESTION D'UN FORMAT EXECUTABLE                           */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour un format d'exécutable générique. */
G_DEFINE_TYPE(GExecutableFormat, g_executable_format, G_TYPE_PROGRAM_FORMAT);


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

static void g_executable_format_class_init(GExecutableFormatClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GKnownFormatClass *known;               /* Version de format connu     */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_executable_format_dispose;
    object->finalize = (GObjectFinalizeFunc)g_executable_format_finalize;

    known = G_KNOWN_FORMAT_CLASS(klass);

    known->analyze = (known_analyze_fc)g_executable_format_analyze;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une instance de format d'exécutable générique.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_executable_format_init(GExecutableFormat *format)
{
    format->portions = NULL;

}


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

static void g_executable_format_dispose(GExecutableFormat *format)
{
    g_clear_object(&format->portions);

    G_OBJECT_CLASS(g_executable_format_parent_class)->dispose(G_OBJECT(format));

}


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

static void g_executable_format_finalize(GExecutableFormat *format)
{
    G_OBJECT_CLASS(g_executable_format_parent_class)->finalize(G_OBJECT(format));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format  = description du format à initialiser pleinement.    *
*                content = contenu binaire à parcourir.                       *
*                                                                             *
*  Description : Met en place un nouveau contenu binaire à analyser.          *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_create(GExecutableFormat *format, GBinContent *content)
{
    bool result;                            /* Bilan à retourner           */
    vmpa2t addr;                            /* Emplacement vide de sens    */
    phys_t length;                          /* Taille de portion globale   */

    result = g_program_format_create(G_PROGRAM_FORMAT(format), content);
    if (!result) goto exit;

    /* Définition de portions */

    /**
     * Avant de lire l'entête du format, on ne sait pas où on se trouve !
     */
    init_vmpa(&addr, 0, VMPA_NO_VIRTUAL);

    length = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content);

    format->portions = g_binary_portion_new(&addr, length);

 exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description du format exécutable à consulter.       *
*                                                                             *
*  Description : Indique le type d'architecture visée par le format.          *
*                                                                             *
*  Retour      : Identifiant de l'architecture ciblée par le format.          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char *g_executable_format_get_target_machine(const GExecutableFormat *format)
{
    char *result;                           /* Désignation à retourner     */
    GExecutableFormatClass *class;          /* Classe de l'instance        */

    class = G_EXECUTABLE_FORMAT_GET_CLASS(format);

    result = class->get_machine(format);

    //assert(result != NULL);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                addr   = adresse principale trouvée si possible. [OUT]       *
*                                                                             *
*  Description : Fournit l'adresse principale associée à un format.           *
*                                                                             *
*  Retour      : Validité de l'adresse transmise.                             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_get_main_address(GExecutableFormat *format, vmpa2t *addr)
{
    bool result;                            /* Bilan à retourner           */
    GExecutableFormatClass *class;          /* Classe de l'instance        */

    class = G_EXECUTABLE_FORMAT_GET_CLASS(format);

    result = class->get_main_addr(format, addr);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format  = description de l'exécutable à modifier.            *
*                portion = portion à inclure dans les définitions du format.  *
*                origin  = source de définition de la portion fournie.        *
*                                                                             *
*  Description : Procède à l'enregistrement d'une portion dans un format.     *
*                                                                             *
*  Retour      : Bilan de l'opération : true si inclusion, false sinon.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_include_portion(GExecutableFormat *format, GBinaryPortion *portion, const vmpa2t *origin)
{
    bool result;                            /* Bilan à retourner           */
    phys_t available;                       /* Taille totale du bianire    */
    const mrange_t *range;                  /* Emplacement de la portion   */
    phys_t start;                           /* Début de zone de la portion */
    vmpa2t no_origin;                       /* Emplacement inconnu         */
    char *msg;                              /* Description d'une erreur    */
    phys_t remaining;                       /* Taille maximale envisageable*/
    bool truncated;                         /* Modification faite ?        */

    result = false;

    available = g_binary_content_compute_size(G_KNOWN_FORMAT(format)->content);

    range = g_binary_portion_get_range(portion);

    start = get_phy_addr(get_mrange_addr(range));

    if (get_mrange_length(range) == 0)
        log_variadic_message(LMT_BAD_BINARY, _("The binary portion '%s' is empty and thus useless... Discarding!"),
                             g_binary_portion_get_desc(portion));

    else if (start >= available)
    {
        if (origin == NULL)
        {
            init_vmpa(&no_origin, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
            origin = &no_origin;
        }

        asprintf(&msg, _("Defined binary portion '%s' is out of the file scope... Discarding!"),
                 g_binary_portion_get_desc(portion));

        //g_binary_format_add_error(G_BIN_FORMAT(format), BFE_STRUCTURE, origin, msg);

        free(msg);

    }

    else
    {
        remaining = available - start;

        truncated = g_binary_portion_limit_range(portion, remaining);

        if (truncated)
            log_variadic_message(LMT_BAD_BINARY, _("Truncated binary portion '%s' to fit the binary content size!"),
                                 g_binary_portion_get_desc(portion));

        result = g_binary_portion_include(format->portions, portion);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                                                                             *
*  Description : Fournit la première couche des portions composent le binaire.*
*                                                                             *
*  Retour      : Arborescence des différentes portions binaires.              *
*                                                                             *
*  Remarques   : Le compteur de références de l'instance renvoyée doit être   *
*                décrémenté après usage.                                      *
*                                                                             *
******************************************************************************/

GBinaryPortion *g_executable_format_get_portions(GExecutableFormat *format)
{
    GBinaryPortion *result;                 /* Instance à retourner        */

    result = format->portions;

    assert(result != NULL);

    ref_object(result);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                off    = position physique à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une position physique. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_offset_into_vmpa(GExecutableFormat *format, phys_t off, vmpa2t *pos)
{
    bool result;                            /* Bilan à retourner           */
    GExecutableFormatClass *class;          /* Classe de l'instance        */

    class = G_EXECUTABLE_FORMAT_GET_CLASS(format);

    result = class->translate_phys(format, off, pos);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                off    = position physique à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une position physique. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_offset_into_vmpa_without_virt(const GExecutableFormat *format, phys_t off, vmpa2t *pos)
{
    init_vmpa(pos, off, VMPA_NO_VIRTUAL);

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                off    = position physique à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une position physique. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_offset_into_vmpa_with_portions(GExecutableFormat *format, phys_t off, vmpa2t *pos)
{
    bool result;                            /* Bilan à retourner           */

    if (format->portions == NULL)
        result = false;

    else
        result = g_binary_portion_translate_offset_into_vmpa(format->portions, off, pos);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                addr   = adresse virtuelle à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_address_into_vmpa(GExecutableFormat *format, virt_t addr, vmpa2t *pos)
{
    bool result;                            /* Bilan à retourner           */
    GExecutableFormatClass *class;          /* Classe de l'instance        */

    class = G_EXECUTABLE_FORMAT_GET_CLASS(format);

    result = class->translate_virt(format, addr, pos);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                addr   = adresse virtuelle à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_address_into_vmpa_without_virt(const GExecutableFormat *format, virt_t addr, vmpa2t *pos)
{
    /**
     * S'il n'y a pas de notion de mémoire virtuelle, positions physiques et
     * adresses virtuelles se confondent.
     *
     * On reste néanmoins cohérent, et on n'utilise donc pas d'adresse virtuelle.
     *
     * Les sauts dans le code renvoient de façon transparente vers des positions
     * physiques.
     */

    init_vmpa(pos, addr, VMPA_NO_VIRTUAL);

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à consulter.            *
*                addr   = adresse virtuelle à retrouver.                      *
*                pos    = position correspondante. [OUT]                      *
*                                                                             *
*  Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_executable_format_translate_address_into_vmpa_with_portions(GExecutableFormat *format, virt_t addr, vmpa2t *pos)
{
    bool result;                            /* Bilan à retourner           */

    if (format->portions == NULL)
        result = false;

    else
        result = g_binary_portion_translate_address_into_vmpa(format->portions, addr, pos);

    return result;

}



/* ---------------------------------------------------------------------------------- */
/*                       IMPLEMENTATION DES FONCTIONS DE CLASSE                       */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : format = format chargé dont l'analyse est lancée.            *
*                                                                             *
*  Description : Assure l'interprétation d'un format en différé.              *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_executable_format_analyze(GExecutableFormat *format)
{
    bool result;                            /* Bilan à retourner           */
    GExecutableFormatClass *class;          /* Classe de l'instance        */

    class = G_EXECUTABLE_FORMAT_GET_CLASS(format);

    if (class->refine_portions != NULL)
        result = class->refine_portions(format);
    else
        result = true;

    return result;

}