/* Chrysalide - Outil d'analyse de fichiers binaires
 * info.c - lecture des informations principales du format DWARF
 *
 * Copyright (C) 2008-2018 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 .
 */
#include "info.h"
#include 
#include 
#include 
#include 
#include 
#include "die.h"
#include "format-int.h"
#include "utils.h"
#define RANGE_ALLOC_BLOCK 100
/* Rassemblement des informations utiles */
typedef struct _work_data
{
    GDwarfFormat *format;                   /* Format à manipuler          */
    mrange_t *ranges;                       /* Espace des DIE à charger    */
} work_data;
/* Procède au chargement d'un DIE de la section debug_info. */
static bool extract_dies_from_debug_info(const work_data *, size_t, GtkStatusStack *, activity_id_t);
/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage DWARF à compléter.         *
*                gid    = groupe de travail impliqué.                         *
                 status = barre de statut à tenir informée.                   *
*                                                                             *
*  Description : Charge les informations depuis une section ".debug_info".    *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool load_dwarf_debug_information(GDwarfFormat *format, wgroup_id_t gid, GtkStatusStack *status)
{
    bool result;                            /* Bilan à renvoyer            */
    GExeFormat *exe;                        /* Exécutable associé          */
    mrange_t range;                         /* Couverture d'une section    */
    GBinContent *content;                   /* Contenu binaire à lire      */
    GBinContent *restricted;                /* Limitation des traitements  */
    vmpa2t stop;                            /* Point d'arrivée à atteindre */
    mrange_t *ranges;                       /* Séquences de zones à traiter*/
    size_t count;                           /* Nombre de ces séquences     */
    size_t allocated;                       /* Quantité d'allocations      */
    SourceEndian endian;                    /* Boutisme du format parent   */
    vmpa2t iter;                            /* Tête de lecture mouvante    */
    vmpa2t start;                           /* Sauvegarde de position      */
    dw_compil_unit_header header;           /* Unité à cerner puis traiter */
    vmpa2t next;                            /* Départ de l'unité suivante  */
    phys_t size;                            /* Taille complète d'une unité */
    work_data data;                         /* Données à communiquer       */
    guint runs_count;                       /* Qté d'exécutions parallèles */
    size_t run_size;                        /* Volume réparti par exécution*/
    GWorkQueue *queue;                      /* Gestionnaire de différés    */
    activity_id_t msg;                      /* Message de progression      */
    guint i;                                /* Boucle de parcours          */
    size_t begin;                           /* Début de bloc de traitement */
    size_t end;                             /* Fin d'un bloc de traitement */
    GSeqWork *work;                         /* Tâche de chargement à lancer*/
    exe = G_DBG_FORMAT(format)->executable;
    result = g_exe_format_get_section_range_by_name(exe, ".debug_info", &range);
    if (result)
    {
        content = G_BIN_FORMAT(format)->content;
        restricted = g_restricted_content_new(content, &range);
        compute_mrange_end_addr(&range, &stop);
        /* Constitution des zones de travail */
        ranges = NULL;
        count = 0;
        allocated = 0;
        endian = g_binary_format_get_endianness(G_BIN_FORMAT(exe));
        for (copy_vmpa(&iter, get_mrange_addr(&range));
             result && cmp_vmpa(&iter, &stop) < 0;
             copy_vmpa(&iter, &next))
        {
            copy_vmpa(&start, &iter);
            result = read_dwarf_compil_unit_header(restricted, &iter, endian, &header, &next);
            if (!result) break;
            if (count == allocated)
            {
                allocated += RANGE_ALLOC_BLOCK;
                ranges = realloc(ranges, allocated * sizeof(mrange_t));
            }
            size = compute_vmpa_diff(&start, &next);
            init_mrange(&ranges[count++], &start, size);
        }
        if (!result)
            goto exit;
        /* Préparation des réceptacles */
        format->info = calloc(count, sizeof(dw_die *));
        format->info_count = count;
        data.format = format;
        data.ranges = ranges;
        /* Lancement des travaux */
        runs_count = get_max_online_threads();
        run_size = count / runs_count;
        queue = get_work_queue();
        msg = gtk_status_stack_add_activity(status, _("Loading all information from the .debug_info section..."),
                                            get_mrange_length(&range));
        for (i = 0; i < runs_count; i++)
        {
            begin = i * run_size;
            if ((i + 1) == runs_count)
                end = count;
            else
                end = begin + run_size;
            work = g_seq_work_new_boolean(&data, begin, end, msg,
                                          (seq_work_bool_cb)extract_dies_from_debug_info, &result);
            g_work_queue_schedule_work(queue, G_DELAYED_WORK(work), gid);
        }
        g_work_queue_wait_for_completion(queue, gid);
        gtk_status_stack_remove_activity(status, msg);
 exit:
        if (ranges != NULL)
            free(ranges);
        g_object_unref(G_OBJECT(restricted));
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : data   = ensemble d'informations utiles à l'opération.       *
*                i      = indice des éléments à traiter.                      *
*                status = barre de statut à tenir informée.                   *
*                id     = identifiant du message affiché à l'utilisateur.     *
*                                                                             *
*  Description : Procède au chargement d'un DIE de la section debug_info.     *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static bool extract_dies_from_debug_info(const work_data *data, size_t i, GtkStatusStack *status, activity_id_t id)
{
    bool result;                            /* Bilan à retourner           */
    GDwarfFormat *format;                   /* Format en cours d'analyse   */
    GBinContent *content;                   /* Contenu binaire à lire      */
    GExeFormat *exe;                        /* Exécutable associé          */
    SourceEndian endian;                    /* Boutisme du format parent   */
    vmpa2t iter;                            /* Tête de lecture mouvante    */
    dw_compil_unit_header header;           /* Unité à cerner puis traiter */
    vmpa2t next;                            /* Départ de l'unité suivante  */
    dw_abbrev_brotherhood *abbrevs;         /* Série d'abréviations        */
    format = data->format;
    /**
     * Comme les informations peuvent aller taper ailleurs dans le binaire
     * (par exemple dans la section debug_str pour certaine valeur, on ne peut
     * pas restreinte le contenu au seul espace traité.
     *
     * L'en-tête lui même a déjà été valide, donc on ne s'embête pas à distinguer
     * différents cas ici.
     */
    content = G_BIN_FORMAT(format)->content;
    exe = G_DBG_FORMAT(format)->executable;
    endian = g_binary_format_get_endianness(G_BIN_FORMAT(exe));
    copy_vmpa(&iter, get_mrange_addr(&data->ranges[i]));
    result = read_dwarf_compil_unit_header(content, &iter, endian, &header, &next);
    if (!result) goto exit;
    abbrevs = load_all_dwarf_abbreviations(format, &header);
    if (abbrevs == NULL) goto exit;
    result = build_dwarf_die(format, content, &iter, &header, abbrevs, &format->info[i]);
    free_all_dwarf_abbreviations(abbrevs);
    gtk_status_stack_update_activity_value(status, id, get_mrange_length(&data->ranges[i]));
 exit:
    return result;
}