/* Chrysalide - Outil d'analyse de fichiers binaires
 * limit.c - détermination des bornes des routines
 *
 * Copyright (C) 2012-2013 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "limit.h"



/* Recherche la zone correspond à une adresse donnée. */
static const mrange_t *find_x_range_for_addr(const mrange_t *, size_t, const vmpa2t *);



/******************************************************************************
*                                                                             *
*  Paramètres  : ranges = liste de zones offrant une exécution et disponibles.*
*                count  = taille de cette liste.                              *
*                                                                             *
*  Description : Recherche la zone correspond à une adresse donnée.           *
*                                                                             *
*  Retour      : Zone trouvée ou NULL si aucune ne correspond.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static const mrange_t *find_x_range_for_addr(const mrange_t *ranges, size_t count, const vmpa2t *addr)
{
    const mrange_t *result;                 /* Zone à retourner            */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;

    for (i = 0; i < count && result == NULL; i++)
        if (mrange_contains_addr(&ranges[i], addr))
            result = &ranges[i];

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : proc     = ensemble d'instructions désassemblées.            *
*                routines = prototypes existants à insérer.                   *
*                count    = quantité de ces prototypes.                       *
*                begin    = point de départ du parcours de liste.             *
*                end      = point d'arrivée exclu du parcours.                *
*                id       = identifiant du message affiché à l'utilisateur.   *
*                                                                             *
*  Description : S'assure qu'une routine est bien bornée.                     *
*                                                                             *
*  Retour      : Tâche créée.                                                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void compute_routine_limit(GBinRoutine *routine, GBinRoutine *prev, GArchProcessor *proc, mrange_t *exe_ranges, size_t exe_count)
{
    const mrange_t *range;                  /* Emplacement courant         */
    vmpa2t addr;                            /* Adresse à conserver         */
    GArchInstruction *start;                /* Première instruction        */
    phys_t diff;                            /* Taille définie par déduction*/
    mrange_t new;                           /* Nouvel emplacement taillé   */

    range = g_binary_routine_get_range(routine);
    if (get_mrange_length(range) > 0) goto crl_skip;

    copy_vmpa(&addr, get_mrange_addr(range));

    /* Marquage de la première instruction */

    start = g_arch_processor_find_instr_by_address(proc, &addr);


    /* FIXME ? */
    if (start == NULL) goto crl_skip;


    g_arch_instruction_set_flag(start, AIF_ROUTINE_START);

    g_object_unref(G_OBJECT(start));

    /* Si on peut se raccrocher à la routine suivante... */
    if (prev != NULL)
    {
        range = g_binary_routine_get_range(prev);

        diff = compute_vmpa_diff(&addr, get_mrange_addr(range));

    }

    /* Sinon on va jusqu'à la fin de la zone ! */
    else
    {
        range = find_x_range_for_addr(exe_ranges, exe_count, &addr);
        if (range == NULL) goto crl_skip;

        diff = compute_vmpa_diff(&addr, get_mrange_addr(range));
        diff = get_mrange_length(range) - diff;

    }

    init_mrange(&new, &addr, diff);

    g_binary_routine_set_range(routine, &new);

 crl_skip:

    ;

}