/* OpenIDA - Outil d'analyse de fichiers binaires
 * abbrev.c - manipulation des abréviation DWARF
 *
 * Copyright (C) 2008 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  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 "abbrev.h"


#include <malloc.h>
#include <stdarg.h>
#include <string.h>


#include "utils.h"



#include <ctype.h>



/* Libère de la mémoire une abréviation DWARF. */
void free_dwarf_abbrev(dw_abbrev *);

/* Charge une abréviations DWARF. */
dw_abbrev *read_dwarf_abbreviations(dwarf_format *, off_t *, uint64_t *);

/* Recherche une abréviation DWARF donnée. */
const dw_abbrev *_find_dwarf_abbreviations(const dw_abbrev *, uint8_t *);

/* Lit la valeur d'un attribut DWARF. */
bool _read_dwarf_abbrev_attribute(dwarf_format *, off_t *, DwarfForm, ...);



/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à compléter.               *
*                                                                             *
*  Description : Charge les abréviations trouvées pour un DWARF.              *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool load_dwarf_abbreviations(dwarf_format *format)
{
    bool result;                            /* Bilan à renvoyer            */




    off_t offset;
    off_t start;
    off_t size;

    bool test;

    int i;


    dw_abbrev *abbrev;
    uint64_t index;

    printf("Searching...\n");


    result = true;

    test = find_exe_section(DBG_FORMAT(format)->e_format, ".debug_abbrev", &start, &size, NULL);

    offset = start;


    printf(" -> offset=%d   size=%d\n", offset, size);



    for (i = 0; i < size; i++)
    {
        if (i % 10 == 0) printf("\n");
        printf("0x%02hhx ", DBG_FORMAT(format)->content[offset + i]);
    }

    printf("\n");




    while (offset < (start + size))
    {
        abbrev = read_dwarf_abbreviations(format, &offset, &index);

        offset++;   /* 0x00 */

        printf("abbrev :: %p\n", abbrev);

        if (abbrev != NULL)
        {
            abbrev->offset -= start;

            format->abbrevs = (dw_abbrev **)realloc(format->abbrevs, ++format->abbrevs_count * sizeof(dw_abbrev *));
            format->abbrevs[format->abbrevs_count - 1] = abbrev;

            printf(" %d attribs, %d children\n", abbrev->attribs_count, abbrev->children_count);

        }
        else
        {
            unload_dwarf_abbreviations(format);
            result = false;
            break;
        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à effacer.                 *
*                                                                             *
*  Description : Décharge les abréviations trouvées pour un DWARF.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void unload_dwarf_abbreviations(dwarf_format *format)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < format->abbrevs_count; i++)
        free_dwarf_abbrev(format->abbrevs[i]);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : abbrev = élément à supprimer de la mémoire.                  *
*                                                                             *
*  Description : Libère de la mémoire une abréviation DWARF.                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void free_dwarf_abbrev(dw_abbrev *abbrev)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < abbrev->children_count; i++)
        free_dwarf_abbrev(abbrev->children[i]);

    free(abbrev->attribs);
    free(abbrev->children);

    free(abbrev);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à compléter.               *
*                pos    = tête de lecture à mettre à jour. [OUT]              *
*                index  = code de l'abréviation. [OUT]                        *
*                                                                             *
*  Description : Charge une abréviation DWARF.                                *
*                                                                             *
*  Retour      : Adresse d'une abréviation ou NULL en cas d'échec.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

dw_abbrev *read_dwarf_abbreviations(dwarf_format *format, off_t *pos, uint64_t *index)
{
    dw_abbrev *result;                      /* Abréviation à retourner     */
    bool has_children;                      /* Indique la présence de fils */
    uint64_t value1;                        /* Valeur quelconque lue #1    */
    uint64_t value2;                        /* Valeur quelconque lue #2    */
    uint64_t sub_index;                     /* Indice d'un sous-élément    */
    dw_abbrev *child;                       /* Sous-élément à intégrer     */

    result = (dw_abbrev *)calloc(1, sizeof(dw_abbrev));

    result->offset = *pos;

    /* Code de l'élément */
    if (!read_uleb128(format, pos, index, true)) goto rda_error;

    if (!read_uleb128(format, pos, &value1, true)) goto rda_error;
    result->tag = value1;

    printf(" --ta :: 0x%02llx\n", value1);

    if (*pos >= DBG_FORMAT(format)->length) goto rda_error;
    has_children = (DBG_FORMAT(format)->content[(*pos)++] == DW_CHILDREN_YES);

    printf(" --ch ? %d\n", has_children);

    /* Liste des attributs */

    while (DBG_FORMAT(format)->content[*pos] != 0x00)
    {
        if (!read_uleb128(format, pos, &value1, true)) goto rda_error;
        if (!read_uleb128(format, pos, &value2, true)) goto rda_error;

        result->attribs = (dw_abbrev_attr *)realloc(result->attribs, ++result->attribs_count * sizeof(dw_abbrev_attr));

        result->attribs[result->attribs_count - 1].attrib = value1;
        result->attribs[result->attribs_count - 1].form = value2;

    }

    (*pos) += 2;    /* 0x00 0x00 */

    /* Chargement des sous-éléments */

    if (has_children)
        while (DBG_FORMAT(format)->content[*pos] != 0x00)
        {
            child = read_dwarf_abbreviations(format, pos, &sub_index);

            if (child == NULL) goto rda_error;

            if ((sub_index - *index - 1) != result->children_count) goto rda_error;

            result->children = (dw_abbrev **)realloc(result->children, ++result->children_count * sizeof(dw_abbrev *));

            result->children[result->children_count - 1] = child;

        }

    return result;

 rda_error:

    free_dwarf_abbrev(result);

    return NULL;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : abbrev = abréviation racine à parcourir.                     *
*                index  = code de l'abréviation. [OUT]                        *
*                                                                             *
*  Description : Recherche une abréviation DWARF donnée.                      *
*                                                                             *
*  Retour      : Adresse d'une abréviation ou NULL en cas d'échec.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const dw_abbrev *_find_dwarf_abbreviations(const dw_abbrev *abbrev, uint8_t *index)
{
    const dw_abbrev *result;                /* Structure à retourner       */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;

    if (*index == 0) result = abbrev;
    else
        for (i = 0; i < abbrev->children_count && result == NULL; i++)
        {
            (*index)--;
            result = _find_dwarf_abbreviations(abbrev->children[i], index);
        }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à consulter.               *
*                offset = position dans les abréviations.                     *
*                pos    = position dans le flux binaire courant. [OUT]        *
*                                                                             *
*  Description : Recherche une abréviation DWARF donnée.                      *
*                                                                             *
*  Retour      : Adresse d'une abréviation ou NULL en cas d'échec.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const dw_abbrev *find_dwarf_abbreviations(dwarf_format *format, const off_t *offset, off_t *pos)
{
    const dw_abbrev *result;                /* Structure à retourner       */
    uint64_t index;                         /* Code de l'abréviation       */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;

    do
    {
        if (!read_uleb128(format, pos, &index, true))
        {
            printf("error skipping padding...\n");
            return NULL;
        }
    }
    while (index == 0);

    for (i = 0; i < format->abbrevs_count; i++)
        if (format->abbrevs[i]->offset == *offset) break;

    if (i < format->abbrevs_count)
    {
        index--;
        result = _find_dwarf_abbreviations(format->abbrevs[i], &index);
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : abbrev = informations à parcourir.                           *
*                attrib = attribut visé par la lecture.                       *
*                                                                             *
*  Description : Indique la présence ou l'absence d'un attribut donné.        *
*                                                                             *
*  Retour      : true si l'attribut est présent, false sinon.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool test_dwarf_abbrev_attribute(const dw_abbrev *abbrev, DwarfAttrib attrib)
{
    bool result;                            /* Bilan à retourner           */
    size_t i;                               /* Boucle de parcours          */

    result = false;

    for (i = 0; i < abbrev->attribs_count && !result; i++)
        result = (abbrev->attribs[i].attrib == attrib);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à compléter.               *
*                pos    = tête de lecture à mettre à jour. [OUT]              *
*                form   = format des données à lire.                          *
*                ...    = lieu d'enregistrement ou NULL. [OUT]                *
*                                                                             *
*  Description : Lit la valeur d'un attribut DWARF.                           *
*                                                                             *
*  Retour      : true si la lecture est un succès, false sinon.               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool _read_dwarf_abbrev_attribute(dwarf_format *format, off_t *pos, DwarfForm form, ...)
{
    bool result;                            /* Bilan à revoyer             */
    va_list ap;                             /* Adresse fournie en dernier  */
    uint8_t *val8;                          /* Données sur 8 bits          */
    uint16_t *val16;                        /* Données sur 16 bits         */
    uint32_t *val32;                        /* Données sur 32 bits         */
    uint64_t *val64;                        /* Données sur 64 bits         */
    uint64_t *sval64;                       /* Données sur 64 bits (signée)*/
    bool *boolval;                          /* Valeur booléenne            */
    uint8_t tmp8;                           /* Données sur 8 bits          */
    uint16_t tmp16;                         /* Données sur 16 bits         */
    uint32_t tmp32;                         /* Données sur 32 bits         */
    uint64_t tmp64;                         /* Données sur 64 bits         */
    uint64_t stmp64;                        /* Données sur 64 bits (signée)*/
    uint64_t size_to_read;                  /* Nombre d'octets à lire      */
    off_t offset;                           /* Décallage dans une zone     */
    char **strval;                          /* Chaîne de caractères        */
    size_t length;                          /* Taille d'une chaîne         */

    va_start(ap, form);

    switch (form)
    {
        case DWF_ADDR:
            result = ((*pos + (format->format == DWF_32_BITS ? 4 : 8)) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val64 = va_arg(ap, uint64_t *);
                if (val64 != NULL)
                {
                    if (format->format == DWF_32_BITS)
                    {
                        memcpy(&tmp32, &DBG_FORMAT(format)->content[*pos], 4);
                        *val64 = tmp32;
                    }
                    else memcpy(val64, &DBG_FORMAT(format)->content[*pos], 8);
                }
                *pos += (format->format == DWF_32_BITS ? 4 : 8);
            }
            break;

        case DWF_BLOCK2:
            result = ((*pos + 2) <= DBG_FORMAT(format)->length);
            if (result)
            {
                memcpy(&tmp16, &DBG_FORMAT(format)->content[*pos], 2);
                size_to_read = tmp16;
                /* ... */
                *pos += 2 + size_to_read;
            }
            break;

        case DWF_BLOCK4:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);
            if (result)
            {
                memcpy(&tmp32, &DBG_FORMAT(format)->content[*pos], 4);
                size_to_read = tmp32;
                /* ... */
                *pos += 4 + size_to_read;
            }
            break;

        case DWF_DATA2:
            result = ((*pos + 2) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val16 = va_arg(ap, uint16_t *);
                if (val16 != NULL) memcpy(val16, &DBG_FORMAT(format)->content[*pos], 2);
                *pos += 2;
            }
            break;

        case DWF_DATA4:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val32 = va_arg(ap, uint32_t *);
                if (val32 != NULL) memcpy(val32, &DBG_FORMAT(format)->content[*pos], 4);
                *pos += 4;
            }
            break;

        case DWF_DATA8:
            result = ((*pos + 8) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val64 = va_arg(ap, uint64_t *);
                if (val64 != NULL) memcpy(val64, &DBG_FORMAT(format)->content[*pos], 8);
                *pos += 8;
            }
            break;

        case DWF_STRING:
            result = ((*pos + 1) <= DBG_FORMAT(format)->length);
            if (result)
            {
                strval = va_arg(ap, char **);
                if (strval != NULL) *strval = (char *)calloc(1, sizeof(char));
                length = 0;

                while (result)
                {
                    if (DBG_FORMAT(format)->content[*pos] == '\0') break;

                    length++;

                    if (strval != NULL)
                    {
                        *strval = (char *)realloc(*strval, (length + 1) * sizeof(char));
                        (*strval)[length - 1] = DBG_FORMAT(format)->content[*pos];
                    }

                    result = ((*pos + 1) <= DBG_FORMAT(format)->length);
                    if (!result) break;

                    (*pos)++;

                }

                result = ((*pos + 1) <= DBG_FORMAT(format)->length);

                if (result)
                {
                    (*pos)++;

                    if (strval != NULL)
                        (*strval)[length] = 0;

                }
                else if (strval != NULL)
                {
                    free(*strval);
                    *strval = NULL;
                }

            }

            break;

        case DWF_BLOCK:
            result = read_uleb128(format, pos, &size_to_read, true);
            result &= ((*pos + size_to_read) <= DBG_FORMAT(format)->length);
            if (result)
            {
                /* ... */
                *pos += size_to_read;
            }
            break;

        case DWF_BLOCK1:
            result = ((*pos + 1) <= DBG_FORMAT(format)->length);
            if (result)
            {
                memcpy(&tmp8, &DBG_FORMAT(format)->content[*pos], 1);
                size_to_read = tmp8;
                /* ... */
                *pos += 1 + size_to_read;
            }
            break;

        case DWF_DATA1:
            result = ((*pos + 1) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val8 = va_arg(ap, uint8_t *);
                if (val8 != NULL) memcpy(val8, &DBG_FORMAT(format)->content[*pos], 1);
                *pos += 1;
            }
            break;

        case DWF_FLAG:
            result = ((*pos + 1) <= DBG_FORMAT(format)->length);
            if (result)
            {
                boolval = va_arg(ap, bool *);
                if (boolval != NULL) *boolval = (DBG_FORMAT(format)->content[*pos] != 0x00);
                *pos += 1;
            }
            break;

        case DWF_SDATA:
            sval64 = va_arg(ap, int64_t *);
            if (sval64 == NULL) sval64 = &stmp64;
            result = read_uleb128(format, pos, sval64, true);
            break;

        case DWF_STRP:
            result = read_abbrev_offset(format, pos, &offset);
            if (result)
            {
                if (va_arg(ap, bool *) != NULL)
                {
                    printf("TODO\n");
                    exit(0);
                }
                /*
                boolval = va_arg(ap, bool *);
                if (boolval != NULL) *boolval = (DBG_FORMAT(format)->content[*pos] != 0x00);
                */
            }
            break;

        case DWF_UDATA:
            val64 = va_arg(ap, uint64_t *);
            if (val64 == NULL) val64 = &tmp64;
            result = read_uleb128(format, pos, val64, true);
            break;

        case DWF_REF_ADDR:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);

            printf("bad at %d\n", __LINE__); exit(0);

            break;


        case DWF_REF1:
            result = ((*pos + 1) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val8 = va_arg(ap, uint8_t *);
                if (val8 != NULL) memcpy(val8, &DBG_FORMAT(format)->content[*pos], 1);
                *pos += 1;
            }
            break;

        case DWF_REF2:
            result = ((*pos + 2) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val16 = va_arg(ap, uint16_t *);
                if (val16 != NULL) memcpy(val16, &DBG_FORMAT(format)->content[*pos], 2);
                *pos += 2;
            }
            break;

        case DWF_REF4:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val32 = va_arg(ap, uint32_t *);
                if (val32 != NULL) memcpy(val32, &DBG_FORMAT(format)->content[*pos], 4);
                *pos += 4;
            }
            break;

        case DWF_REF8:
            result = ((*pos + 8) <= DBG_FORMAT(format)->length);
            if (result)
            {
                val64 = va_arg(ap, uint64_t *);
                if (val64 != NULL) memcpy(val64, &DBG_FORMAT(format)->content[*pos], 8);
                *pos += 8;
            }
            break;

        case DWF_REF_UDATA:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);

            printf("bad at %d\n", __LINE__); exit(0);

            break;


        case DWF_INDIRECT:
            result = ((*pos + 4) <= DBG_FORMAT(format)->length);

            printf("bad at %d\n", __LINE__); exit(0);

            break;




        default:
            result = false;
            break;

    }

    va_end(ap);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à compléter.               *
*                pos    = tête de lecture à mettre à jour. [OUT]              *
*                update = indique si la position est à mettre à jour.         *
*                abbrev = informations à parcourir.                           *
*                attrib = attribut visé par la lecture.                       *
*                ...    = lieu d'enregistrement ou NULL. [OUT]                *
*                                                                             *
*  Description : Lit la valeur d'un attribut DWARF.                           *
*                                                                             *
*  Retour      : true si la lecture est un succès, false sinon.               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool read_dwarf_abbrev_attribute(dwarf_format *format, off_t *pos, bool update, const dw_abbrev *abbrev, DwarfAttrib attrib, ...)
{
    bool result;                            /* Bilan à retourner           */
    off_t curpos;                           /* Tête de lecture effective   */
    size_t i;                               /* Boucle de parcours          */
    va_list ap;                             /* Adresse fournie en dernier  */

    result = true;

    curpos = *pos;

    for (i = 0; i < abbrev->attribs_count && result; i++)
        if (abbrev->attribs[i].attrib == attrib) break;
        else result = _read_dwarf_abbrev_attribute(format, &curpos, abbrev->attribs[i].form, NULL);

    if (result)
    {
        va_start(ap, attrib);

        if (i < abbrev->attribs_count)
            result = _read_dwarf_abbrev_attribute(format, &curpos, abbrev->attribs[i].form, va_arg(ap, void *));
        else
            result = false;

        va_end(ap);

    }

    if (result && update) *pos = curpos;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations de débogage à compléter.               *
*                pos    = tête de lecture à mettre à jour. [OUT]              *
*                abbrev = informations à survoler.                            *
*                                                                             *
*  Description : Fait avancer la tête de lecture d'une seule abréviation.     *
*                                                                             *
*  Retour      : true si l'opération est un succès, false sinon.              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool skip_dwarf_abbrev(dwarf_format *format, off_t *pos, const dw_abbrev *abbrev)
{
    bool result;                            /* Bilan à revoyer             */
    size_t i;                               /* Boucle de parcours          */
    uint64_t index;                         /* Code de padding             */

    result = true;

    /* Ecartement du corps */

    for (i = 0; i < abbrev->attribs_count && result; i++)
        result = _read_dwarf_abbrev_attribute(format, pos, abbrev->attribs[i].form, NULL);

    /* Ecartement du padding */

    do
    {
        if (!read_uleb128(format, pos, &index, false))
        {
            printf("error skipping padding...\n");
            return false;
        }

        if (index == 0)
            read_uleb128(format, pos, &index, true);

    }
    while (index == 0);

    return result;

}