/* Chrysalide - Outil d'analyse de fichiers binaires
 * header.c - annotation des en-têtes de section de binaires ELF
 *
 * Copyright (C) 2015 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  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 "section.h"


#include <i18n.h>
#include <arch/raw.h>
#include <common/extstr.h>
#include <format/symbol.h>
#include <format/elf/elf-int.h>
#include <format/elf/section.h>



/* Charge tous les symboles liés à un en-tête de section ELF. */
static bool annotate_elf_section_header(GElfFormat *, SourceEndian, const elf_shdr *, vmpa2t *);



/******************************************************************************
*                                                                             *
*  Paramètres  : format  = description de l'exécutable à compléter.           *
*                endian  = boutisme présentement utilisé.                     *
*                strings = section renvoyant vers des chaînes de caractères.  *
*                pos     = tête de lecture à déplacer. [OUT]                  *
*                                                                             *
*  Description : Charge tous les symboles liés à un en-tête de section ELF.   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool annotate_elf_section_header(GElfFormat *format, SourceEndian endian, const elf_shdr *strings, vmpa2t *pos)
{
    elf_shdr shdr;                          /* En-tête de programme ELF    */
    GBinContent *content;                   /* Contenu binaire à lire      */
    const char *secname;                    /* Nom d'une section analysée  */
    ImmOperandDisplay disp;                 /* Afficahge de valeur         */
    const char *text;                       /* Texte constant à insérer    */
    vmpa2t start;                           /* Localisation des symboles   */
    GArchInstruction *instr;                /* Instruction décodée         */
    GArchOperand *operand;                  /* Opérande à venir modifier   */
    GDbComment *comment;                    /* Définition de commentaire   */
    GBinSymbol *symbol;                     /* Symbole à intégrer          */
    char *dtext;                            /* Texte dynamique à créer     */
    bool filled;                            /* Suivi de mise en place      */

    if (!read_elf_section_header(format, get_phy_addr(pos), &shdr))
        return false;

    content = g_binary_format_get_content(G_BIN_FORMAT(format));

    /* Champ "sh_name" */

    secname = extract_name_from_elf_string_section(format, strings,
                                                   ELF_SHDR(format, shdr, sh_name));

    if (secname == NULL)
        dtext = strdup(_("Section name: <invalid>"));
    else
    {
        dtext = strdup(_("Section name: '"));
        dtext = stradd(dtext, secname);
        dtext = stradd(dtext, "'");
    }

    copy_vmpa(&start, pos);
    instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

    SET_IMM_DISPLAY(instr, operand, 0, IOD_DEC);

    ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, dtext);

    free(dtext);

    /* Champ "sh_type" */

    disp = IOD_DEC;

    switch (ELF_SHDR(format, shdr, sh_type))
    {
        case SHT_NULL:
            text = _("Section type: unused");
            break;
        case SHT_PROGBITS:
            text = _("Section type: program data");
            break;
        case SHT_SYMTAB:
            text = _("Section type: symbol table");
            break;
        case SHT_STRTAB:
            text = _("Section type: string table");
            break;
        case SHT_RELA:
            text = _("Section type: relocation entries with addends");
            break;
        case SHT_HASH:
            text = _("Section type: symbol hash table");
            break;
        case SHT_DYNAMIC:
            text = _("Section type: dynamic linking information");
            break;
        case SHT_NOTE:
            text = _("Section type: notes");
            break;
        case SHT_NOBITS:
            text = _("Section type: program space with no data (bss)");
            break;
        case SHT_REL:
            text = _("Section type: relocation entries, no addends");
            break;
        case SHT_SHLIB:
            text = _("Section type: reserved");
            break;
        case SHT_DYNSYM:
            text = _("Section type: dynamic linker symbol table");
            break;
        case SHT_INIT_ARRAY:
            text = _("Section type: array of constructors");
            break;
        case SHT_FINI_ARRAY:
            text = _("Section type: array of destructors");
            break;
        case SHT_PREINIT_ARRAY:
            text = _("Section type: array of pre-constructors");
            break;
        case SHT_GROUP:
            text = _("Section type: section group");
            break;
        case SHT_SYMTAB_SHNDX:
            text = _("Section type: extended section indeces");
            break;
        case SHT_LOOS ... SHT_HIOS:
            disp = IOD_HEX;
            switch (ELF_SHDR(format, shdr, sh_type))
            {
                case SHT_GNU_ATTRIBUTES:
                    text = _("Section type: object attributes");
                    break;
                case SHT_GNU_HASH:
                    text = _("Section type: GNU-style hash table");
                    break;
                case SHT_GNU_LIBLIST:
                    text = _("Section type: prelink library list");
                    break;
                case SHT_CHECKSUM:
                    text = _("Section type: checksum for DSO content");
                    break;
                case SHT_LOSUNW ... SHT_HISUNW:
                    switch (ELF_SHDR(format, shdr, sh_type))
                    {
                        case SHT_SUNW_move:
                            text = _("Section type: SHT_SUNW_move");
                            break;
                        case SHT_SUNW_COMDAT:
                            text = _("Section type: SHT_SUNW_COMDAT");
                            break;
                        case SHT_SUNW_syminfo:
                            text = _("Section type: SHT_SUNW_syminfo");
                            break;
                        case SHT_GNU_verdef:
                            text = _("Section type: version definition section");
                            break;
                        case SHT_GNU_verneed:
                            text = _("Section type: version needs section");
                            break;
                        case SHT_GNU_versym:
                            text = _("Section type: version symbol table");
                            break;
                        default:
                            text = _("Section type: Sun-specific");
                            break;
                    }
                    break;
                default:
                    text = _("Section type: OS-specific");
                    break;
            }
            break;
        case SHT_LOPROC ... SHT_HIPROC:
            disp = IOD_HEX;
            text = _("Section type: processor-specific");
            break;
        case SHT_LOUSER ... SHT_HIUSER:
            disp = IOD_HEX;
            text = _("Section type: application-specific");
            break;
        default:
            disp = IOD_HEX;
            text = _("Section type: unknown");
            break;
    }

    copy_vmpa(&start, pos);
    instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

    SET_IMM_DISPLAY(instr, operand, 0, disp);

    ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, text);

    /* Champ "sh_flags"... */

    dtext = strdup(_("Section flags: "));
    filled = false;

    if (ELF_SHDR(format, shdr, sh_type) & SHF_WRITE)
    {
        dtext = stradd(dtext, "W");
        filled = true;
    }

    if (ELF_SHDR(format, shdr, sh_type) & SHF_ALLOC)
    {
        dtext = stradd(dtext, "A");
        filled = true;
    }

    if (ELF_SHDR(format, shdr, sh_type) & SHF_EXECINSTR)
    {
        dtext = stradd(dtext, "X");
        filled = true;
    }

    if (ELF_SHDR(format, shdr, sh_type) & SHF_MERGE)
    {
        dtext = stradd(dtext, "M");
        filled = true;
    }

    if (ELF_SHDR(format, shdr, sh_type) & SHF_LINK_ORDER)
    {
        dtext = stradd(dtext, "L");
        filled = true;
    }

    if (ELF_SHDR(format, shdr, sh_type) & SHF_TLS)
    {
        dtext = stradd(dtext, "T");
        filled = true;
    }

    if (!filled)
        dtext = stradd(dtext, _("none"));

    if (format->is_32b)
    {
        /* Champ "sh_flags" (suite) */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, dtext);

        free(dtext);

        /* Champ "sh_addr" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section virtual addr at execution"));

        /* Champ "sh_offset" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section file offset"));

        /* Champ "sh_size" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        SET_IMM_DISPLAY(instr, operand, 0, IOD_DEC);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section size in bytes"));

    }
    else
    {
        /* Champ "sh_flags" (suite) */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, dtext);

        free(dtext);

        /* Champ "sh_addr" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section virtual addr at execution"));

        /* Champ "sh_offset" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section file offset"));

        /* Champ "sh_size" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        SET_IMM_DISPLAY(instr, operand, 0, IOD_DEC);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section size in bytes"));

    }

    /* Champ "sh_link" */

    copy_vmpa(&start, pos);
    instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

    ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Link to another section"));

    /* Champ "sh_info" */

    copy_vmpa(&start, pos);
    instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

    ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Additional section information"));

    if (format->is_32b)
    {
        /* Champ "sh_addralign" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section alignment"));

        /* Champ "sh_entsize" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_32_BITS, 1, pos, endian);

        SET_IMM_DISPLAY(instr, operand, 0, IOD_DEC);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Entry size if section holds table"));

    }
    else
    {
        /* Champ "sh_addralign" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Section alignment"));

        /* Champ "sh_entsize" */

        copy_vmpa(&start, pos);
        instr = g_raw_instruction_new_array(content, MDS_64_BITS, 1, pos, endian);

        SET_IMM_DISPLAY(instr, operand, 0, IOD_DEC);

        ADD_RAW_AS_SYM(format, symbol, &start, instr, comment, _("Entry size if section holds table"));

    }

    g_object_unref(G_OBJECT(content));

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : format = description de l'exécutable à compléter.            *
*                                                                             *
*  Description : Charge tous les symboles liés aux en-têtes de section ELF.   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool annotate_elf_section_header_table(GElfFormat *format)
{
    bool result;                            /* Bilan à retourner           */
    const elf_header *header;               /* En-tête principale          */
    SourceEndian endian;                    /* Boutisme utilisé            */
    elf_shdr strings;                       /* Section des descriptions    */
    off_t offset;                           /* Tête de lecture du binaire  */
    vmpa2t pos;                             /* Localisation des symboles   */
    uint16_t e_shnum;                       /* Nombre d'éléments 'Program' */
    uint16_t i;                             /* Boucle de parcours          */

    result = true;

    header = g_elf_format_get_header(format);
    endian = g_elf_format_get_endianness(format);

    if (!find_elf_section_by_index(format, ELF_HDR(format, *header, e_shstrndx), &strings))
        return false;

    offset = ELF_HDR(format, *header, e_shoff);

    init_vmpa(&pos, offset, 0x9900);

    e_shnum = ELF_HDR(format, *header, e_shnum);

    for (i = 0; i < e_shnum && result; i++)
        result = annotate_elf_section_header(format, endian, &strings, &pos);

    return true;

}