/* Chrysalide - Outil d'analyse de fichiers binaires
 * post.c - traitements complémentaires à la phase de désassemblage
 *
 * Copyright (C) 2014 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 .
 */
#include "link.h"
#include 
#include 
/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction ARMv7 à traiter.                       *
*                context = contexte associé à la phase de désassemblage.      *
*                format  = acès aux données du binaire d'origine.             *
*                iset    = type de jeu d'instructions courant à faire suivre. *
*                                                                             *
*  Description : Complète un désassemblage accompli pour une instruction.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void handle_links_with_instruction_b_with_orig(GArchInstruction *instr, GArmV7Context *context, GBinFormat *format, ArmV7InstrSet iset)
{
    const mrange_t *range;                  /* Emplacementt d'instruction  */
    virt_t pc;                              /* Position dans l'exécution   */
    GArchOperand *op;                       /* Opérande numérique en place */
    int32_t offset;                         /* Décallage encodé en dur     */
    virt_t target;                          /* Adresse virtuelle visée     */
    range = g_arch_instruction_get_range(instr);
    pc = get_virt_addr(get_mrange_addr(range));
    pc += get_mrange_length(range);
    op = g_arch_instruction_get_operand(instr, 0);
    if (g_imm_operand_get_value(G_IMM_OPERAND(op), MDS_32_BITS_SIGNED, &offset))
        g_imm_operand_set_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, pc + offset);
    else assert(0);
    printf("1... 0x%x (0x%x) + 0x%x\n",
           (unsigned int)get_virt_addr(get_mrange_addr(range)),
           (unsigned int)pc, (unsigned int)offset);
    target = pc + offset;
    //g_armv7_context_define_encoding(context, target, iset);
    g_armv7_context_push_drop_point_ext(context, target, iset);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction ARMv7 à traiter.                       *
*                context = contexte associé à la phase de désassemblage.      *
*                format  = acès aux données du binaire d'origine.             *
*                iset    = type de jeu d'instructions courant à faire suivre. *
*                                                                             *
*  Description : Complète un désassemblage accompli pour une instruction.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void handle_links_with_instruction_bl_with_orig(GArchInstruction *instr, GArmV7Context *context, GBinFormat *format, ArmV7InstrSet iset)
{
    const mrange_t *range;                  /* Emplacementt d'instruction  */
    virt_t pc;                              /* Position dans l'exécution   */
    GArchOperand *op;                       /* Opérande numérique en place */
    int32_t offset;                         /* Décallage encodé en dur     */
    virt_t target;                          /* Adresse virtuelle visée     */
    range = g_arch_instruction_get_range(instr);
    pc = get_virt_addr(get_mrange_addr(range));
    /**
     * Qu'on se trouve en mode Thumb ou ARM, l'instruction
     * ne peut qu'être encodée sur 4 octets.
     */
    assert(get_mrange_length(range) == 4);
    pc += 4;
    op = g_arch_instruction_get_operand(instr, 0);
    if (g_imm_operand_get_value(G_IMM_OPERAND(op), MDS_32_BITS_SIGNED, &offset))
        g_imm_operand_set_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, pc + offset);
    else assert(0);
    printf("2... 0x%x (0x%x) + 0x%x\n",
           (unsigned int)get_virt_addr(get_mrange_addr(range)),
           (unsigned int)pc, (unsigned int)offset);
    target = pc + offset;
    //g_armv7_context_define_encoding(context, target, iset);
    g_armv7_context_push_drop_point_ext(context, target, iset);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction ARMv7 à traiter.                       *
*                context = contexte associé à la phase de désassemblage.      *
*                format  = acès aux données du binaire d'origine.             *
*                iset    = type de jeu d'instructions courant à inverser.     *
*                                                                             *
*  Description : Complète un désassemblage accompli pour une instruction.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void handle_links_with_instruction_blx_with_dest(GArchInstruction *instr, GArmV7Context *context, GBinFormat *format, ArmV7InstrSet iset)
{
    const mrange_t *range;                  /* Emplacementt d'instruction  */
    virt_t pc;                              /* Position dans l'exécution   */
    GArchOperand *op;                       /* Opérande numérique en place */
    int32_t offset;                         /* Décallage encodé en dur     */
    virt_t target;                          /* Adresse virtuelle visée     */
    range = g_arch_instruction_get_range(instr);
    pc = get_virt_addr(get_mrange_addr(range));
    /**
     * Qu'on se trouve en mode Thumb ou ARM, l'instruction
     * ne peut qu'être encodée sur 4 octets.
     */
    assert(get_mrange_length(range) == 4);
    pc += 4;
    pc -= pc % 4;
    op = g_arch_instruction_get_operand(instr, 0);
    if (g_imm_operand_get_value(G_IMM_OPERAND(op), MDS_32_BITS_SIGNED, &offset))
        g_imm_operand_set_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, pc + offset);
    else assert(0);
    printf("3... 0x%x (0x%x) + 0x%x\n",
           (unsigned int)get_virt_addr(get_mrange_addr(range)),
           (unsigned int)pc, (unsigned int)offset);
    target = pc + offset;
    //g_armv7_context_define_encoding(context, target, iset);
    g_armv7_context_push_drop_point_ext(context, target, iset);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction ARMv7 à traiter.                       *
*                context = contexte associé à la phase de désassemblage.      *
*                format  = acès aux données du binaire d'origine.             *
*                                                                             *
*  Description : Complète un désassemblage accompli pour une instruction.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void handle_links_with_instruction_cb_n_z(GArchInstruction *instr, GArmV7Context *context, GBinFormat *format)
{
    const mrange_t *range;                  /* Emplacementt d'instruction  */
    virt_t pc;                              /* Position dans l'exécution   */
    GArchOperand *op;                       /* Opérande numérique en place */
    uint32_t offset;                        /* Décallage encodé en dur     */
    virt_t target;                          /* Adresse virtuelle visée     */
    range = g_arch_instruction_get_range(instr);
    pc = get_virt_addr(get_mrange_addr(range));
    printf("PC :: 0x%08x\n", (unsigned int)pc);
    /**
     * En mode Thumb, pc a pour valeur l'adresse courante plus 4.
     */
    pc += 4;
    op = g_arch_instruction_get_operand(instr, 1);
    if (g_imm_operand_get_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, &offset))
        g_imm_operand_set_value(G_IMM_OPERAND(op), MDS_32_BITS_UNSIGNED, pc + offset);
    else assert(0);
    target = pc + offset;
    //g_armv7_context_define_encoding(context, target, AV7IS_THUMB);
    g_armv7_context_push_drop_point_ext(context, target, AV7IS_THUMB);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : instr   = instruction ARMv7 à traiter.                       *
*                context = contexte associé à la phase de désassemblage.      *
*                format  = acès aux données du binaire d'origine.             *
*                iset    = type de jeu d'instructions courant à inverser.     *
*                                                                             *
*  Description : Complète un désassemblage accompli pour une instruction.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void handle_links_with_instruction_ldr_literal_with_orig(GArchInstruction *instr, GArmV7Context *context, GBinFormat *format, ArmV7InstrSet iset)
{
    const mrange_t *range;                  /* Emplacementt d'instruction  */
    phys_t phys_pc;                         /* Position dans l'exécution   */
    GArchOperand *op;                       /* Opérande de surcouche       */
    GArchOperand *sub_op;                   /* Opérande numérique en place */
    uint32_t offset;                        /* Décallage encodé en dur     */
    bool ret;                               /* Bilan d'une récupération    */
    off_t val_offset;                       /* Position de valeur à lire   */
    off_t length;                           /* Taille des données à lire   */
    const bin_t *data;                      /* Données binaires à lire     */
    uint32_t target;                        /* Adresse virtuelle visée     */
    GArchOperand *new;                      /* Instruction de ciblage      */
    /* Récupération de l'adresse visée par le chargement */
    range = g_arch_instruction_get_range(instr);
    phys_pc = get_phy_addr(get_mrange_addr(range));
    phys_pc &= ~3;
    //phys_pc = (phys_pc + 3) & ~3;
    printf(">>>>>>> @pc @ 0x%08x\n", (unsigned int)phys_pc);
    switch (iset)
    {
        case AV7IS_ARM:
            phys_pc += 8;
            break;
        case AV7IS_THUMB:
            phys_pc += 4;
            break;
        default:
            assert(0);
            break;
    }
    op = g_arch_instruction_get_operand(instr, 1);
    assert(G_IS_ARMV7_OFFSET_OPERAND(op));
    sub_op = g_armv7_offset_operand_get_value(G_ARMV7_OFFSET_OPERAND(op));
    ret = g_imm_operand_get_value(G_IMM_OPERAND(sub_op), MDS_32_BITS_UNSIGNED, &offset);
    if (!ret)
    {
        assert(0);
        return;
    }
    /* Lecture de la valeur vers laquelle renvoyer */
    if (g_armv7_offset_operand_is_positive(G_ARMV7_OFFSET_OPERAND(op)))
        val_offset = phys_pc + offset;
    else
        val_offset = phys_pc - offset;
    data = g_binary_format_get_content(format, &length);
    printf(">>>>>>> @reading @ 0x%08x (0x%x)\n", (unsigned int)val_offset, offset);
    ret = read_u32(&target, data, &val_offset, length, SRE_LITTLE /* FIXME */);
    if (!ret) return;
    printf(">>>>>>> @got target :: 0x%08x\n", (unsigned int)target);
    //g_imm_operand_set_value(G_IMM_OPERAND(sub_op), MDS_32_BITS_UNSIGNED, target);
    new = g_imm_operand_new_from_value(MDS_32_BITS_UNSIGNED, target);
    g_arch_instruction_replace_operand(instr, new, op);
    //exit(0);
    //target = pc + offset;
    //g_armv7_context_define_encoding(context, target, AV7IS_THUMB);
    g_armv7_context_push_drop_point_ext(context, target, AV7IS_THUMB);
    //exit(0);
}