/* OpenIDA - Outil d'analyse de fichiers binaires
 * e_elf.c - support du format ELF
 *
 * 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 .
 */
#include "e_elf.h"
#include 
#include 
#include "elf-int.h"
#include "section.h"
#include "strings.h"
#include "symbol.h"
#include "../../panel/log.h"
#define _(str) str
/* Fournit l'adresse mémoire du point d'entrée du programme. */
uint64_t get_elf_entry_point(const elf_format *);
/* S'assure qu'une chaîne de caractère tient sur une ligne. */
extern char *escape_crlf_bin_string(char *);
/* Récupère tous les éléments identifiées dans le binaire. */
size_t get_elf_resolved_items(const elf_format *, char ***, ResolvedType **, uint64_t **);
/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire à parcourir.                       *
*                length  = taille du contenu en question.                     *
*                                                                             *
*  Description : Indique si le format peut être pris en charge ici.           *
*                                                                             *
*  Retour      : true si la réponse est positive, false sinon.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool elf_is_matching(const uint8_t *content, off_t length)
{
    bool result;                            /* Bilan à faire connaître     */
    result = false;
    if (length >= 4)
        result = (strncmp((const char *)content, "\x7f\x45\x4c\x46" /* .ELF */, 4) == 0);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire à parcourir.                       *
*                length  = taille du contenu en question.                     *
*                                                                             *
*  Description : Prend en charge un nouvel ELF.                               *
*                                                                             *
*  Retour      : Adresse de la structure mise en place ou NULL en cas d'échec.*
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
elf_format *load_elf(const uint8_t *content, off_t length)
{
    elf_format *result;                     /* Structure à retourner       */
    bool test;                              /* Bilan d'une initialisation  */
    Elf32_Half i;
    Elf32_Phdr phdr;
    size_t count;
    result = (elf_format *)calloc(1, sizeof(elf_format));
    EXE_FORMAT(result)->content = content;
    EXE_FORMAT(result)->length = length;
    EXE_FORMAT(result)->get_entry_point = (get_entry_point_fc)get_elf_entry_point;
    EXE_FORMAT(result)->get_def_parts = (get_def_parts_fc)get_elf_default_code_parts;
    EXE_FORMAT(result)->find_section = (find_section_fc)find_elf_section_content_by_name;
    EXE_FORMAT(result)->get_symbols = (get_symbols_fc)get_elf_symbols;
    EXE_FORMAT(result)->get_resolved = (get_resolved_fc)get_elf_resolved_items;
    EXE_FORMAT(result)->resolve_symbol = (resolve_symbol_fc)resolve_elf_symbol;
    EXE_FORMAT(result)->get_all_routines = (get_all_routines_fc)get_all_elf_routines;
    memcpy(&result->header, content, sizeof(Elf32_Ehdr));
    /* TODO : endian */
    /* Vérification des tailles d'entrée de table */
    switch (result->header.e_ident[EI_CLASS])
    {
        case ELFCLASS32:
            if (result->header.e_phentsize != sizeof(Elf32_Phdr))
            {
                log_variadic_message(LMT_BAD_BINARY, _("Corrupted program header size (%hu); fixed !"),
                                     result->header.e_phentsize);
                result->header.e_phentsize = sizeof(Elf32_Phdr);
            }
            if (result->header.e_shentsize != sizeof(Elf32_Shdr))
            {
                log_variadic_message(LMT_BAD_BINARY, _("Corrupted section header size (%hu); fixed !"),
                                     result->header.e_shentsize);
                result->header.e_shentsize = sizeof(Elf32_Shdr);
            }
            break;
        case ELFCLASS64:
            if (result->header.e_phentsize != sizeof(Elf64_Phdr))
            {
                log_variadic_message(LMT_BAD_BINARY, _("Corrupted program header size (%hu); fixed !"),
                                     result->header.e_phentsize);
                result->header.e_phentsize = sizeof(Elf64_Phdr);
            }
            if (result->header.e_shentsize != sizeof(Elf64_Shdr))
            {
                log_variadic_message(LMT_BAD_BINARY, _("Corrupted section header size (%hu); fixed !"),
                                     result->header.e_shentsize);
                result->header.e_shentsize = sizeof(Elf64_Shdr);
            }
            break;
        default:
            log_variadic_message(LMT_BAD_BINARY, ("Invalid ELF class '%hhu'"),
                                 result->header.e_ident[EI_CLASS]);
            break;
    }
    /* FIXME : à améliorer */
    if ((result->header.e_shnum * result->header.e_shentsize) >= length)
    {
        log_variadic_message(LMT_BAD_BINARY, ("Suspicious section table (bigger than the binary !) ; reset !"));
        result->header.e_shnum = 0;
    }
    result->is_32b = (result->header.e_ident[EI_CLASS] == ELFCLASS32);
    for (i = 0; i < result->header.e_phnum; i++)
    {
        memcpy(&phdr, &content[result->header.e_phoff + i * result->header.e_phentsize], result->header.e_phentsize);
        printf(" seg [0x%08x] :: %d -> %d\n", phdr.p_type, phdr.p_offset, phdr.p_filesz);
    }
    test = read_elf_section_names(result);
    printf("ok ? %d\n", test);
    test = find_all_elf_strings(result);
    printf("ok ? %d\n", test);
    test = load_elf_symbols(result);
    printf("ok ? %d\n", test);
    return result;
 lelf:
    /* TODO */
    return NULL;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                                                                             *
*  Description : Fournit l'adresse mémoire du point d'entrée du programme.    *
*                                                                             *
*  Retour      : Adresse de mémoire.                                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
uint64_t get_elf_entry_point(const elf_format *format)
{
    return format->header.e_entry;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                count  = quantité de zones listées. [OUT]                    *
*                                                                             *
*  Description : Fournit les références aux zones de code à analyser.         *
*                                                                             *
*  Retour      : Zones de code à analyser.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bin_part **get_elf_default_code_parts(const elf_format *format, size_t *count)
{
    bin_part **result;                      /* Tableau à retourner         */
    bin_part *part;                         /* Partie à intégrer à la liste*/
    off_t offset;                           /* Position physique           */
    off_t size;                             /* Taille de la partie         */
    uint64_t voffset;                       /* Adresse virtuelle éventuelle*/
    int i;                                  /* Boucle de parcours          */
    Elf_Shdr shdr;                          /* En-tête de section ELF      */
    Elf_Phdr phdr;                          /* En-tête de programme ELF    */
    result = NULL;
    *count = 0;
    if (format->sec_size > 0)
    {
        if (find_elf_section_content_by_name(format, ".plt", &offset, &size, &voffset))
        {
            part = create_bin_part();
            set_bin_part_name(part, ".plt");
            set_bin_part_values(part, offset, size, voffset);
            result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
            result[*count - 1] = part;
        }
        if (find_elf_section_content_by_name(format, ".init", &offset, &size, &voffset))
        {
            part = create_bin_part();
            set_bin_part_name(part, ".init");
            set_bin_part_values(part, offset, size, voffset);
            result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
            result[*count - 1] = part;
        }
        if (find_elf_section_content_by_name(format, ".text", &offset, &size, &voffset))
        {
            part = create_bin_part();
            set_bin_part_name(part, ".text");
            set_bin_part_values(part, offset, size, voffset);
            result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
            result[*count - 1] = part;
        }
        if (find_elf_section_content_by_name(format, ".fini", &offset, &size, &voffset))
        {
            part = create_bin_part();
            set_bin_part_name(part, ".fini");
            set_bin_part_values(part, offset, size, voffset);
            result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
            result[*count - 1] = part;
        }
    }
    /* Si aucune section n'a été trouvée... */
    if (*count == 0)
        for (i = 0; i < format->header.e_shnum; i++)
        {
            offset = format->header.e_shoff + format->header.e_shentsize * i;
            if ((offset + format->header.e_shentsize) >= EXE_FORMAT(format)->length) continue;
            memcpy(&shdr, &EXE_FORMAT(format)->content[offset], format->header.e_shentsize);
            if (ELF_SHDR(format, &shdr, sh_flags) & SHF_EXECINSTR)
            {
                part = create_bin_part();
                /* TODO : nom */
                set_bin_part_values(part, ELF_SHDR(format, &shdr, sh_offset),
                                    ELF_SHDR(format, &shdr, sh_size),
                                    ELF_SHDR(format, &shdr, sh_addr));
                result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
                result[*count - 1] = part;
            }
        }
    /* En désespoir de cause, on se rabbat sur les parties de programme directement */
    if (*count == 0)
        for (i = 0; i < format->header.e_phnum; i++)
        {
            offset = format->header.e_phoff + format->header.e_phentsize * i;
            if ((offset + format->header.e_phentsize) >= EXE_FORMAT(format)->length) continue;
            memcpy(&phdr, &EXE_FORMAT(format)->content[offset], format->header.e_phentsize);
            if (ELF_PHDR(format, &phdr, p_flags) & PF_X)
            {
                part = create_bin_part();
                /* TODO : nom */
                set_bin_part_values(part, ELF_PHDR(format, &phdr, p_offset),
                                    ELF_PHDR(format, &phdr, p_filesz),
                                    ELF_PHDR(format, &phdr, p_vaddr));
                result = (bin_part **)realloc(result, ++(*count) * sizeof(bin_part *));
                result[*count - 1] = part;
            }
        }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format  = informations chargées à consulter.                 *
*                labels  = liste des commentaires à insérer. [OUT]            *
*                types   = type des symboles listés. [OUT]                    *
*                offsets = liste des indices des commentaires. [OUT]          *
*                                                                             *
*  Description : Récupère tous les symboles présents dans le contenu binaire. *
*                                                                             *
*  Retour      : Nombre d'éléments mis en place.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
size_t get_elf_symbols(const elf_format *format, char ***labels, SymbolType **types, uint64_t **offsets)
{
    size_t result;                          /* Quantité à retourner        */
    size_t i;                               /* Boucle de parcours          */
    result = format->sym_count;
    *labels = (char **)calloc(result, sizeof(char *));
    *types = (SymbolType *)calloc(result, sizeof(SymbolType));
    *offsets = (uint64_t *)calloc(result, sizeof(uint64_t));
    for (i = 0; i < format->sym_count; i++)
    {
        (*labels)[i] = strdup(format->symbols[i].name);
        (*types)[i] = STP_SECTION;
        (*offsets)[i] = format->symbols[i].address;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format  = informations chargées à consulter.                 *
*                labels  = liste des commentaires à insérer. [OUT]            *
*                types   = type des symboles listés. [OUT]                    *
*                offsets = liste des indices des commentaires. [OUT]          *
*                                                                             *
*  Description : Récupère tous les éléments identifiées dans le binaire.      *
*                                                                             *
*  Retour      : Nombre d'éléments mis en place.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
size_t get_elf_resolved_items(const elf_format *format, char ***labels, ResolvedType **types, uint64_t **offsets)
{
    size_t result;                          /* Quantité à retourner        */
    size_t i;                               /* Boucle de parcours          */
    size_t start;                           /* Point de départ du tour     */
    result = format->sym_count + format->str_count;
    *labels = (char **)calloc(result, sizeof(char *));
    *types = (SymbolType *)calloc(result, sizeof(SymbolType));
    *offsets = (uint64_t *)calloc(result, sizeof(uint64_t));
    for (i = 0; i < format->sym_count; i++)
    {
        (*labels)[i] = strdup(format->symbols[i].name);
        (*types)[i] = RTP_SECTION;
        (*offsets)[i] = format->symbols[i].address;
    }
    start = format->sym_count;
    for (i = 0; i < format->str_count; i++)
    {
        (*labels)[start + i] = strndup(format->strings[i].value, format->strings[i].len);
        (*types)[start + i] = RTP_STRING;
        (*offsets)[start + i] = format->strings[i].vaddress;
        (*labels)[start + i] = escape_crlf_bin_string((*labels)[start + i]);
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                label  = étiquette du symbole si trouvé. [OUT]               *
*                type   = type du symbole trouvé. [OUT]                       *
*                offset = adresse à cibler, puis décallage final. [OUT]       *
*                                                                             *
*  Description : Recherche le symbole correspondant à une adresse.            *
*                                                                             *
*  Retour      : true si l'opération a été un succès, false sinon.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool resolve_elf_symbol(const elf_format *format, char **label, SymbolType *type, uint64_t *offset)
{
    bool result;                            /* Bilan à retourner           */
    size_t best_index;                      /* Meilleur symbole trouvé     */
    uint64_t best_addr;                     /* Meilleure adresse trouvée   */
    size_t i;                               /* Boucle de parcours          */
    if (resolve_elf_strings(format, label, offset))
    {
        *type = STP_STRING;
        return true;
    }
    best_addr = UINT64_MAX;
    for (i = 0; i < format->sym_count; i++)
        if (format->symbols[i].address <= *offset && (*offset - format->symbols[i].address) < best_addr)
        {
            best_index = i;
            best_addr = *offset - format->symbols[i].address;
        }
    result = (best_addr != UINT64_MAX);
    if (result)
    {
        *label = strdup(format->symbols[best_index].name);
        *type = STP_SECTION;
        *offset -= format->symbols[best_index].address;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : format = informations chargées à consulter.                  *
*                count  = taille du tableau créé. [OUT]                       *
*                                                                             *
*  Description : Fournit le prototype de toutes les routines détectées.       *
*                                                                             *
*  Retour      : Tableau créé ou NULL si aucun symbole de routine trouvé.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bin_routine **get_all_elf_routines(const elf_format *format, size_t *count)
{
    bin_routine **result;                   /* Tableau à retourner         */
    size_t i;                               /* Boucle de parcours          */
    result = (bin_routine **)calloc(format->sym_count, sizeof(bin_routine *));
    *count = format->sym_count;
    for (i = 0; i < format->sym_count; i++)
    {
        result[i] = create_binary_routine();
        set_binary_routine_offset(result[i], format->symbols[i].address);
        set_binary_routine_name(result[i], strdup(format->symbols[i].name));
    }
    return result;
}