/* Chrysalide - Outil d'analyse de fichiers binaires
* processor.c - gestion générique des architectures
*
* Copyright (C) 2008-2019 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 "processor.h"
#include
#include
#include
#include
#include
#include "instruction-int.h"
#include "processor-int.h"
#include "../common/sort.h"
#include "../core/logs.h"
#include "../glibext/chrysamarshal.h"
/* Initialise la classe générique des processeurs. */
static void g_arch_processor_class_init(GArchProcessorClass *);
/* Initialise une instance de processeur d'architecture. */
static void g_arch_processor_init(GArchProcessor *);
/* Supprime toutes les références externes. */
static void g_arch_processor_dispose(GArchProcessor *);
/* Procède à la libération totale de la mémoire. */
static void g_arch_processor_finalize(GArchProcessor *);
/* Fournit un contexte générique pour l'exécution du processeur. */
static GProcContext *_g_arch_processor_get_context(const GArchProcessor *);
/* ------------------ MANIPULATIONS DES INSTRUCTIONS DESASSEMBLEES ------------------ */
/* Démarre la définition d'un nouveau groupe d'instructions. */
static void g_arch_processor_add_new_coverage(GArchProcessor *, GArchInstruction *, size_t);
/* Termine la définition d'un nouveau groupe d'instructions. */
static void g_arch_processor_finish_last_coverage(GArchProcessor *, GArchInstruction *, size_t);
/* Coupe un groupe d'instructions en deux. */
static instr_coverage *g_arch_processor_split_coverages(GArchProcessor *, instr_coverage *, GArchInstruction *, size_t);
/* Fusionne deux groupes d'instructions. */
static void g_arch_processor_merge_coverages(GArchProcessor *, instr_coverage *, instr_coverage *);
/* Met à jour une série de groupes d'instructions. */
static void g_arch_processor_update_coverages(GArchProcessor *, instr_coverage *, bool);
/* Recherche rapidement un indice d'instruction via une adresse. */
static bool g_arch_processor_find_covered_index_by_address(const GArchProcessor *, const instr_coverage *, const vmpa2t *, bool, size_t *);
/* Indique le type défini pour un processeur d'architecture. */
G_DEFINE_TYPE(GArchProcessor, g_arch_processor, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe générique des processeurs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_class_init(GArchProcessorClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_processor_dispose;
object->finalize = (GObjectFinalizeFunc)g_arch_processor_finalize;
klass->get_ctx = _g_arch_processor_get_context;
g_signal_new("changed",
G_TYPE_ARCH_PROCESSOR,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GArchProcessorClass, changed),
NULL, NULL,
g_cclosure_user_marshal_VOID__OBJECT_BOOLEAN,
G_TYPE_NONE, 2, G_TYPE_ARCH_INSTRUCTION, G_TYPE_BOOLEAN);
}
/******************************************************************************
* *
* Paramètres : proc = instance à initialiser. *
* *
* Description : Initialise une instance de processeur d'architecture. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_init(GArchProcessor *proc)
{
g_mutex_init(&proc->mutex);
#ifndef NDEBUG
g_atomic_int_set(&proc->locked, 0);
#endif
proc->errors = NULL;
proc->error_count = 0;
g_mutex_init(&proc->error_mutex);
#ifndef NDEBUG
g_atomic_int_set(&proc->error_locked, 0);
#endif
proc->coverages = NULL;
proc->cov_allocated = 0;
proc->cov_count = 0;
}
/******************************************************************************
* *
* Paramètres : proc = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_dispose(GArchProcessor *proc)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < proc->instr_count; i++)
{
/**
* Pour éviter un cycle de maintien des références, on détruit tous
* les liens depuis l'extérieur !
*/
if (proc->instructions[i] != NULL)
g_arch_instruction_delete_all_links(proc->instructions[i]);
g_clear_object(&proc->instructions[i]);
}
g_mutex_clear(&proc->mutex);
g_mutex_clear(&proc->error_mutex);
G_OBJECT_CLASS(g_arch_processor_parent_class)->dispose(G_OBJECT(proc));
}
/******************************************************************************
* *
* Paramètres : proc = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_finalize(GArchProcessor *proc)
{
size_t i; /* Boucle de parcours */
if (proc->instructions != NULL)
free(proc->instructions);
if (proc->errors != NULL)
{
for (i = 0; i < proc->error_count; i++)
if (proc->errors[i].desc != NULL)
free(proc->errors[i].desc);
free(proc->errors);
}
if (proc->coverages != NULL)
free(proc->coverages);
G_OBJECT_CLASS(g_arch_processor_parent_class)->finalize(G_OBJECT(proc));
}
/******************************************************************************
* *
* Paramètres : proc = architecture, spectatrice ici. *
* *
* Description : Fournit un contexte générique pour l'exécution du processeur.*
* *
* Retour : Contexte mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
static GProcContext *_g_arch_processor_get_context(const GArchProcessor *proc)
{
GProcContext *result; /* Instance à retourner */
result = g_object_new(G_TYPE_PROC_CONTEXT, NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Fournit la désignation interne du processeur d'architecture. *
* *
* Retour : Simple chaîne de caractères. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_arch_processor_get_key(const GArchProcessor *proc)
{
const char *result; /* Désignation à renvoyer */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->key;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Fournit le nom humain de l'architecture visée. *
* *
* Retour : Désignation humaine associée au processeur. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_arch_processor_get_desc(const GArchProcessor *proc)
{
const char *result; /* Désignation à renvoyer */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->desc;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Fournit le boustime du processeur d'une architecture. *
* *
* Retour : Boutisme associé au processeur. *
* *
* Remarques : - *
* *
******************************************************************************/
SourceEndian g_arch_processor_get_endianness(const GArchProcessor *proc)
{
SourceEndian result; /* Boutisme à retourner */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->endianness;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Fournit la taille de l'espace mémoire d'une architecture. *
* *
* Retour : Taille de l'espace mémoire. *
* *
* Remarques : - *
* *
******************************************************************************/
MemoryDataSize g_arch_processor_get_memory_size(const GArchProcessor *proc)
{
MemoryDataSize result; /* Taille à retourner */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->memsize;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Fournit la taille min. des instructions d'une architecture. *
* *
* Retour : Taille d'encodage des instructions. *
* *
* Remarques : - *
* *
******************************************************************************/
MemoryDataSize g_arch_processor_get_instruction_min_size(const GArchProcessor *proc)
{
MemoryDataSize result; /* Taille à retourner */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->inssize;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur d'architecture à consulter. *
* *
* Description : Indique si l'architecture possède un espace virtuel ou non. *
* *
* Retour : true si un espace virtuel existe, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_arch_processor_has_virtual_space(const GArchProcessor *proc)
{
bool result; /* Indication à retourner */
GArchProcessorClass *class; /* Classe de l'instance */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->virt_space;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* *
* Description : Fournit un contexte propre au processeur d'une architecture. *
* *
* Retour : Nouveau contexte mis à disposition. *
* *
* Remarques : - *
* *
******************************************************************************/
GProcContext *g_arch_processor_get_context(const GArchProcessor *proc)
{
GProcContext *result; /* Contexte à retourner */
GArchProcessorClass *class; /* Classe de l'instance active */
class = G_ARCH_PROCESSOR_GET_CLASS(proc);
result = class->get_ctx(proc);
return result;
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* ctx = contexte lié à l'exécution du processeur. *
* content = flux de données à analyser. *
* pos = position courante dans ce flux. [OUT] *
* format = format du fichier contenant le code. *
* *
* Description : Désassemble une instruction dans un flux de données. *
* *
* Retour : Instruction mise en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_processor_disassemble(const GArchProcessor *proc, GProcContext *ctx, const GBinContent *content, vmpa2t *pos, GExeFormat *format)
{
GArchInstruction *result; /* Instruction à renvoyer */
vmpa2t back; /* Position sauvegardée */
assert(has_phys_addr(pos));
copy_vmpa(&back, pos);
result = G_ARCH_PROCESSOR_GET_CLASS(proc)->disassemble(proc, ctx, content, pos, format);
if (result == NULL)
copy_vmpa(pos, &back);
return result;
}
/* ---------------------------------------------------------------------------------- */
/* RASSEMBLEMENT DES INSTRUCTIONS DESASSEMBLEES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : proc = architecture à manipuler. *
* state = nouvel état de l'accès aux instructions. *
* *
* Description : Protège ou lève la protection de l'accès aux instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_lock_unlock(GArchProcessor *proc, bool state)
{
if (state)
{
g_mutex_lock(&proc->mutex);
#ifndef NDEBUG
g_atomic_int_set(&proc->locked, 1);
#endif
}
else
{
#ifndef NDEBUG
g_atomic_int_set(&proc->locked, 0);
#endif
g_mutex_unlock(&proc->mutex);
}
}
/******************************************************************************
* *
* Paramètres : proc = architecture à consulter via la procédure. *
* *
* Description : Fournit la marque de dernière modification des instructions. *
* *
* Retour : Marque de la dernière modification de la liste d'instruct°. *
* *
* Remarques : - *
* *
******************************************************************************/
unsigned int g_arch_processor_get_stamp(const GArchProcessor *proc)
{
return proc->stamp;
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* *
* Description : Compte le nombre d'instructions représentées. *
* *
* Retour : Nombre d'instructions présentes. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_processor_count_instructions(const GArchProcessor *proc)
{
assert(g_atomic_int_get(&proc->locked) == 1);
return proc->instr_count;
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* list = liste des instructions désassemblées. *
* count = taille de cette liste. *
* *
* Description : Note les instructions désassemblées avec une architecture. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_set_instructions(GArchProcessor *proc, GArchInstruction **list, size_t count)
{
GArchInstruction *last; /* Dernière instruction traitée*/
size_t i; /* Boucle de parcours */
GArchInstruction *instr; /* Instruction à analyser */
g_arch_processor_lock(proc);
proc->instructions = list;
proc->instr_count = count;
proc->stamp++;
last = NULL;
for (i = 0; i < count; i++)
{
instr = list[i];
/* Constitution des groupes */
if (last == NULL || g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START)
{
if (last != NULL)
g_arch_processor_finish_last_coverage(proc, last, i - 1);
g_arch_processor_add_new_coverage(proc, instr, i);
}
last = instr;
}
if (last != NULL)
g_arch_processor_finish_last_coverage(proc, last, proc->instr_count - 1);
g_arch_processor_unlock(proc);
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* instr = instruction à ajouter à l'ensemble. *
* *
* Description : Ajoute une instruction désassemblée à la liste. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_add_instruction(GArchProcessor *proc, GArchInstruction *instr)
{
const mrange_t *range; /* Couverture de l'instruction */
size_t ins_index; /* Indice de l'instruction */
#ifndef NDEBUG
bool found; /* Présence d'un élément */
#endif
instr_coverage *coverage; /* Couverture fine impactée */
instr_coverage new; /* Nouveau groupe à insérer */
g_arch_processor_lock(proc);
/**
* Ajout de l'instruction.
*/
int compare_instructions(const GArchInstruction **a, const GArchInstruction **b)
{
const mrange_t *range_a; /* Couverture d'instruction A */
const mrange_t *range_b; /* Couverture d'instruction B */
range_a = g_arch_instruction_get_range(*a);
range_b = g_arch_instruction_get_range(*b);
return cmp_mrange(range_a, range_b);
}
#ifndef NDEBUG
found = bsearch_index(&instr, proc->instructions, proc->instr_count,
sizeof(GArchInstruction *), (__compar_fn_t)compare_instructions, &ins_index);
assert(!found);
#else
bsearch_index(&instr, proc->instructions, proc->instr_count,
sizeof(GArchInstruction *), (__compar_fn_t)compare_instructions, &ins_index);
#endif
proc->instructions = _qinsert(proc->instructions, &proc->instr_count,
sizeof(GArchInstruction *), &instr, ins_index);
/**
* Actualisation des couvertures de code.
*/
range = g_arch_instruction_get_range(instr);
coverage = (instr_coverage *)g_arch_processor_find_coverage_by_address(proc, get_mrange_addr(range));
if (coverage)
{
if (g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START)
{
coverage = g_arch_processor_split_coverages(proc, coverage, instr, ins_index);
if (coverage != NULL)
g_arch_processor_update_coverages(proc, coverage, true);
}
else
g_arch_processor_update_coverages(proc, coverage, true);
}
else
{
/**
* Il n'existe pas de couverture pour l'instruction ajoutée.
*
* Donc on en crée une dédiée, et on l'insère au bon endroit.
*/
copy_mrange(&new.range, range);
new.start = ins_index;
new.count = 1;
int compare_coverages(const instr_coverage *a, const instr_coverage *b)
{
return cmp_mrange(&a->range, &b->range);
}
proc->coverages = qinsert(proc->coverages, &proc->cov_count,
sizeof(instr_coverage), (__compar_fn_t)compare_coverages, &new);
}
proc->stamp++;
g_arch_processor_unlock(proc);
g_signal_emit_by_name(proc, "changed", instr, true);
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* instr = instruction à retirer de l'ensemble. *
* *
* Description : Retire une instruction désassemblée de la liste. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_remove_instruction(GArchProcessor *proc, GArchInstruction *instr)
{
bool emit; /* Ordre d'émission de signal */
const mrange_t *irange; /* Couverture de l'instruction */
instr_coverage *coverage; /* Couverture fine impactée */
VMPA_BUFFER(pos); /* Traduction de position */
size_t index; /* Indice de l'instruction */
bool status; /* Bilan d'une recherche */
emit = false;
g_arch_instruction_delete_all_links(instr);
irange = g_arch_instruction_get_range(instr);
g_arch_processor_lock(proc);
coverage = (instr_coverage *)g_arch_processor_find_coverage_by_address(proc, get_mrange_addr(irange));
/**
* L'instruction doit se trouve dans l'ensemble des instructions enregistrées.
* Donc une couverture associée doit exister.
*/
assert(coverage != NULL);
if (coverage == NULL)
{
vmpa2_phys_to_string(get_mrange_addr(irange), MDS_UNDEFINED, pos, NULL);
log_variadic_message(LMT_ERROR, _("Can not find coverage for instruction @ %s"), pos);
goto done;
}
/**
* Mises à jour des groupes d'instructions.
*/
/* Si l'instruction est en début de couverture */
if (cmp_vmpa(get_mrange_addr(&coverage->range), get_mrange_addr(irange)) == 0)
{
if (coverage == proc->coverages)
copy_vmpa(get_mrange_addr(&coverage->range), get_mrange_addr(irange));
else
{
assert(g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START);
g_arch_processor_merge_coverages(proc, coverage - 1, coverage);
coverage = (instr_coverage *)g_arch_processor_find_coverage_by_address(proc, get_mrange_addr(irange));
assert(coverage != NULL);
}
}
set_mrange_length(&coverage->range, get_mrange_length(&coverage->range) - get_mrange_length(irange));
g_arch_processor_update_coverages(proc, coverage, false);
/**
* Retrait effectif de l'instruction.
*/
status = g_arch_processor_find_covered_index_by_address(proc, coverage, get_mrange_addr(irange), false, &index);
assert(status);
if (!status)
{
vmpa2_phys_to_string(get_mrange_addr(irange), MDS_UNDEFINED, pos, NULL);
log_variadic_message(LMT_ERROR, _("Can not find instruction @ %s"), pos);
goto done;
}
if ((index + 1) < proc->instr_count)
memmove(&proc->instructions[index], &proc->instructions[index + 1],
(proc->instr_count - index - 1) * sizeof(GArchInstruction *));
proc->instr_count--;
proc->instructions = (GArchInstruction **)realloc(proc->instructions,
proc->instr_count * sizeof(GArchInstruction *));
proc->stamp++;
emit = true;
done:
g_arch_processor_unlock(proc);
if (emit)
g_signal_emit_by_name(proc, "changed", instr, false);
g_object_unref(G_OBJECT(instr));
}
/******************************************************************************
* *
* Paramètres : proc = architecture visée par la procédure. *
* index = indice de l'instruction visée. *
* *
* Description : Fournit une instruction désassemblée pour une architecture. *
* *
* Retour : Instructions désassemblée trouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_processor_get_instruction(const GArchProcessor *proc, size_t index)
{
GArchInstruction *result; /* Instruction à retourner */
assert(g_atomic_int_get(&proc->locked) == 1);
if (proc->instr_count == 0)
result = NULL;
else
{
assert(index < proc->instr_count);
result = proc->instructions[index];
assert(result != NULL);
g_object_ref(G_OBJECT(result));
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* CONSERVATION DES SOUCIS DURANT LE CHARGEMENT */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : proc = architecture à manipuler. *
* state = nouvel état de l'accès aux erreurs relevées. *
* *
* Description : Protège ou lève la protection de l'accès aux erreurs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_lock_unlock_errors(GArchProcessor *proc, bool state)
{
if (state)
{
g_mutex_lock(&proc->error_mutex);
#ifndef NDEBUG
g_atomic_int_set(&proc->error_locked, 1);
#endif
}
else
{
#ifndef NDEBUG
g_atomic_int_set(&proc->error_locked, 0);
#endif
g_mutex_unlock(&proc->error_mutex);
}
}
/******************************************************************************
* *
* Paramètres : proc = architecture concernée par la procédure. *
* index = indice du problème visé. *
* type = type d'erreur retrouvée. *
* addr = localisation associée. *
* desc = éventuelle description humaine de description. *
* *
* Description : Etend la liste des soucis détectés avec de nouvelles infos. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_processor_add_error(GArchProcessor *proc, ArchProcessingError type, const vmpa2t *addr, const char *desc)
{
proc_error *error; /* Raccourci de confort */
g_arch_processor_lock_errors(proc);
proc->errors = realloc(proc->errors, ++proc->error_count * sizeof(proc_error));
error = &proc->errors[proc->error_count - 1];
error->type = type;
copy_vmpa(&error->addr, addr);
if (desc != NULL)
error->desc = strdup(desc);
else
error->desc = NULL;
g_arch_processor_unlock_errors(proc);
}
/******************************************************************************
* *
* Paramètres : proc = architecture à consulter durant la procédure. *
* *
* Description : Indique le nombre d'erreurs relevées au niveau assembleur. *
* *
* Retour : Nombre d'erreurs en stock. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_processor_count_errors(GArchProcessor *proc)
{
size_t result; /* Quantité à retourner */
assert(g_atomic_int_get(&proc->error_locked) == 1);
result = proc->error_count;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = architecture concernée par la procédure. *
* index = indice du problème visé. *
* type = type d'erreur retrouvée. [OUT] *
* addr = localisation associée. [OUT] *
* desc = éventuelle description humaine de description. [OUT] *
* *
* Description : Fournit les éléments concernant un soucis détecté. *
* *
* Retour : Validité des informations renseignées. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_arch_processor_get_error(GArchProcessor *proc, size_t index, ArchProcessingError *type, vmpa2t *addr, char **desc)
{
bool result; /* Bilan à retourner */
proc_error *error; /* Raccourci de confort */
assert(g_atomic_int_get(&proc->error_locked) == 1);
result = (index < proc->error_count);
assert(result);
if (result)
{
error = &proc->errors[index];
*type = error->type;
copy_vmpa(addr, &error->addr);
if (error->desc != NULL)
*desc = strdup(error->desc);
else
*desc = NULL;
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* MANIPULATIONS DES INSTRUCTIONS DESASSEMBLEES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : proc = architecture à comléter par la procédure. *
* first = première instruction d'un nouveau groupe. *
* start = indice de cette instruction dans l'ensemble global. *
* *
* Description : Démarre la définition d'un nouveau groupe d'instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_add_new_coverage(GArchProcessor *proc, GArchInstruction *first, size_t start)
{
instr_coverage *coverage; /* Couverture à définir */
const mrange_t *irange; /* Couverture de l'instruction */
assert(g_atomic_int_get(&proc->locked) == 1);
/* Mise à disposition de d'avantage d'espace */
if (proc->cov_allocated == proc->cov_count)
{
proc->cov_allocated += COV_ALLOC_BLOCK;
proc->coverages = realloc(proc->coverages, proc->cov_allocated * sizeof(instr_coverage));
}
coverage = &proc->coverages[proc->cov_count++];
irange = g_arch_instruction_get_range(first);
init_mrange(&coverage->range, get_mrange_addr(irange), 0);
coverage->start = start;
}
/******************************************************************************
* *
* Paramètres : proc = architecture à comléter par la procédure. *
* last = dernière instruction d'un nouveau groupe. *
* end = indice de cette instruction dans l'ensemble global. *
* *
* Description : Termine la définition d'un nouveau groupe d'instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_finish_last_coverage(GArchProcessor *proc, GArchInstruction *last, size_t end)
{
instr_coverage *coverage; /* Couverture à définir */
const mrange_t *irange; /* Couverture de l'instruction */
phys_t diff; /* Ecart entre les extrémités */
assert(g_atomic_int_get(&proc->locked) == 1);
coverage = &proc->coverages[proc->cov_count - 1];
irange = g_arch_instruction_get_range(last);
diff = compute_vmpa_diff(get_mrange_addr(&coverage->range), get_mrange_addr(irange));
diff += get_mrange_length(irange);
set_mrange_length(&coverage->range, diff);
coverage->count = end - coverage->start + 1;
}
/******************************************************************************
* *
* Paramètres : proc = architecture à modifier via la procédure. *
* coverage = couverture comprenant l'instruction fournie. *
* instr = point de coupure du groupe en question. *
* ins_index = indice de l'instruction dans la liste. *
* *
* Description : Coupe un groupe d'instructions en deux. *
* *
* Retour : Première couverture avec les indices décalés. *
* *
* Remarques : - *
* *
******************************************************************************/
static instr_coverage *g_arch_processor_split_coverages(GArchProcessor *proc, instr_coverage *coverage, GArchInstruction *instr, size_t ins_index)
{
instr_coverage *result; /* Groupe suivant à renvoyer */
const mrange_t *range; /* Emplacement de l'instruction*/
size_t cov_index; /* Indice du groupe indiqué */
bool first; /* Première instruction ? */
size_t remaining; /* Qté d'instructions restantes*/
vmpa2t end; /* Position finale d'un groupe */
phys_t diff; /* Taille d'un groupe */
/* Tour de situation */
range = g_arch_instruction_get_range(instr);
cov_index = coverage - proc->coverages;
first = (ins_index == coverage->start);
/* Mise en place d'une zone supplémentaire */
if (proc->cov_allocated == proc->cov_count)
{
proc->cov_allocated += COV_ALLOC_BLOCK;
proc->coverages = (instr_coverage *)realloc(proc->coverages,
proc->cov_allocated * sizeof(instr_coverage));
}
assert(cov_index < proc->cov_count);
memmove(&proc->coverages[cov_index + 1], &proc->coverages[cov_index],
(proc->cov_count - cov_index) * sizeof(instr_coverage));
proc->cov_count++;
/* Actualisation des informations */
if (first)
{
/* Première zone */
coverage = &proc->coverages[cov_index];
copy_mrange(&coverage->range, range);
coverage->start = ins_index;
coverage->count = 1;
/* Seconde zone et zones suivantes */
result = &proc->coverages[cov_index + 1];
}
else
{
/* Première zone */
coverage = &proc->coverages[cov_index];
remaining = coverage->count;
compute_mrange_end_addr(&coverage->range, &end);
diff = compute_vmpa_diff(get_mrange_addr(&coverage->range), get_mrange_addr(range));
set_mrange_length(&coverage->range, diff);
coverage->count = ins_index - coverage->start;
assert(coverage->count > 0);
remaining -= coverage->count;
/* Seconde zone */
coverage = &proc->coverages[cov_index + 1];
diff = compute_vmpa_diff(get_mrange_addr(range), &end);
init_mrange(&coverage->range, get_mrange_addr(range), diff);
coverage->start = ins_index;
coverage->count = 1 + remaining;
/* Zones suivantes */
if ((cov_index + 2) < proc->cov_count)
result = &proc->coverages[cov_index + 2];
else
result = NULL;
}
return result;
}
/******************************************************************************
* *
* Paramètres : proc = architecture à modifier via la procédure. *
* a = premier groupe d'instructions à traiter. *
* b = second groupe d'instructions à traiter. *
* *
* Description : Fusionne deux groupes d'instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_merge_coverages(GArchProcessor *proc, instr_coverage *a, instr_coverage *b)
{
vmpa2t end; /* Limite de la couverture B */
phys_t diff; /* Ecart entre les extrémités */
size_t b_idx; /* Indice de la couverture B */
assert(g_atomic_int_get(&proc->locked) == 1);
assert(a < b);
compute_mrange_end_addr(&b->range, &end);
diff = compute_vmpa_diff(get_mrange_addr(&a->range), &end);
set_mrange_length(&a->range, diff);
a->count += b->count;
b_idx = (b - proc->coverages);
assert(b_idx < proc->cov_count);
if ((b_idx + 1) < proc->cov_count)
memmove(b, b + 1, (proc->cov_count - b_idx - 1) * sizeof(instr_coverage));
proc->cov_count--;
}
/******************************************************************************
* *
* Paramètres : proc = architecture à modifier via la procédure. *
* first = premier groupe d'instructions à traiter. *
* add = nature de la modification. *
* *
* Description : Met à jour une série de groupes d'instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_processor_update_coverages(GArchProcessor *proc, instr_coverage *first, bool add)
{
size_t index; /* Indice de la couverture */
size_t i; /* Boucle de parcours */
assert(g_atomic_int_get(&proc->locked) == 1);
index = first - proc->coverages;
for (i = index; i < proc->cov_count; i++)
{
if (add)
proc->coverages[i].count++;
else
proc->coverages[i].count--;
}
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* addr = position en mémoire ou physique à chercher. *
* *
* Description : Recherche un groupe d'instruction d'après son adresse. *
* *
* Retour : Couverture trouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
const instr_coverage *g_arch_processor_find_coverage_by_address(const GArchProcessor *proc, const vmpa2t *addr)
{
instr_coverage *result; /* Trouvaille à retourner */
void *ptr; /* Résultat des recherches */
//assert(g_atomic_int_get(&proc->locked) == 1);
int search_for_coverage_by_addr(const vmpa2t *a, const instr_coverage *c)
{
int status; /* Bilan d'une comparaison */
status = cmp_mrange_with_vmpa(&c->range, a);
return status;
}
ptr = bsearch(addr, proc->coverages, proc->cov_count,
sizeof(instr_coverage), (__compar_fn_t)search_for_coverage_by_addr);
result = ((instr_coverage *)ptr);
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* coverage = zone de couverture fine à fouiller. *
* addr = position en mémoire ou physique à chercher. *
* nearby = la recherche s'effectue-t-elle de façon stricte ? *
* index = indice de l'instruction trouvée. [OUT] *
* *
* Description : Recherche rapidement un indice d'instruction via une adresse.*
* *
* Retour : Validité de l'indice fourni. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_arch_processor_find_covered_index_by_address(const GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby, size_t *index)
{
bool result; /* Bilan à faire remonter */
void *ptr; /* Résultat des recherches */
__compar_fn_t fn; /* Fonction auxiliaire adaptée */
//assert(g_atomic_int_get(&proc->locked) == 1);
int search_for_instr_by_addr(const vmpa2t *a, const GArchInstruction **b)
{
const mrange_t *range_b; /* Emplacement pour l'instr. B */
range_b = g_arch_instruction_get_range(*b);
return cmp_vmpa(a, get_mrange_addr(range_b));
}
int search_for_instr_by_nearby_addr(const vmpa2t *a, const GArchInstruction **b)
{
const mrange_t *range_b; /* Emplacement pour l'instr. B */
range_b = g_arch_instruction_get_range(*b);
return cmp_mrange_with_vmpa(range_b, a);
}
if (nearby)
fn = (__compar_fn_t)search_for_instr_by_nearby_addr;
else
fn = (__compar_fn_t)search_for_instr_by_addr;
ptr = bsearch(addr, &proc->instructions[coverage->start], coverage->count,
sizeof(GArchInstruction *), fn);
if (ptr == NULL)
result = false;
else
{
result = true;
*index = ((GArchInstruction **)ptr) - proc->instructions;
}
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* addr = position en mémoire ou physique à chercher. *
* nearby = la recherche s'effectue-t-elle de façon stricte ? *
* *
* Description : Recherche une instruction d'après son adresse. *
* *
* Retour : Instruction trouvée à l'adresse donnée, NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *_g_arch_processor_find_instr_by_address(GArchProcessor *proc, const vmpa2t *addr, bool nearby)
{
GArchInstruction *result; /* Trouvaille à retourner */
const instr_coverage *coverage; /* Couverture fine à fouiller */
g_arch_processor_lock(proc);
coverage = g_arch_processor_find_coverage_by_address(proc, addr);
if (coverage != NULL)
result = _g_arch_processor_find_covered_instr_by_address(proc, coverage, addr, nearby);
else
result = NULL;
g_arch_processor_unlock(proc);
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* coverage = zone de couverture fine à fouiller. *
* addr = position en mémoire ou physique à chercher. *
* nearby = la recherche s'effectue-t-elle de façon stricte ? *
* *
* Description : Recherche rapidement une instruction d'après son adresse. *
* *
* Retour : Instruction trouvée à l'adresse donnée, NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *_g_arch_processor_find_covered_instr_by_address(const GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby)
{
GArchInstruction *result; /* Trouvaille à retourner */
size_t index; /* Indice d'instruction visée */
bool valid; /* Validité de l'indice */
valid = g_arch_processor_find_covered_index_by_address(proc, coverage, addr, nearby, &index);
if (valid)
result = g_arch_processor_get_instruction(proc, index);
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* addr = position en mémoire ou physique à chercher. *
* nearby = la recherche s'effectue-t-elle de façon stricte ? *
* *
* Description : Met en place un itérateur d'instruction selon une adresse. *
* *
* Retour : Itérateur mis en place, ou NULL si l'opération est un échec. *
* *
* Remarques : - *
* *
******************************************************************************/
instr_iter_t *_g_arch_processor_get_iter_from_address(GArchProcessor *proc, const vmpa2t *addr, bool nearby)
{
instr_iter_t *result; /* Itérateur à retourner */
const instr_coverage *coverage; /* Couverture fine à fouiller */
g_arch_processor_lock(proc);
coverage = g_arch_processor_find_coverage_by_address(proc, addr);
if (coverage != NULL)
result = _g_arch_processor_get_covered_iter_from_address(proc, coverage, addr, nearby);
else
result = NULL;
g_arch_processor_unlock(proc);
return result;
}
/******************************************************************************
* *
* Paramètres : proc = processeur recensant diverses instructions. *
* coverage = zone de couverture fine à fouiller. *
* addr = position en mémoire ou physique à chercher. *
* nearby = la recherche s'effectue-t-elle de façon stricte ? *
* *
* Description : Met en place un itérateur d'instruction selon une adresse. *
* *
* Retour : Itérateur mis en place, ou NULL si l'opération est un échec. *
* *
* Remarques : - *
* *
******************************************************************************/
instr_iter_t *_g_arch_processor_get_covered_iter_from_address(GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby)
{
instr_iter_t *result; /* Itérateur à retourner */
size_t index; /* Indice d'instruction visée */
bool valid; /* Validité de l'indice */
valid = g_arch_processor_find_covered_index_by_address(proc, coverage, addr, nearby, &index);
if (valid)
result = create_instruction_iterator(proc, index);
else
result = NULL;
return result;
}