/* Chrysalide - Outil d'analyse de fichiers binaires
* format.c - support des différents formats binaires
*
* Copyright (C) 2009-2020 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 "format.h"
#include
#include
#include
#include "format-int.h"
#include "preload.h"
#include "../arch/processor.h"
#include "../common/sort.h"
#include "../plugins/pglist.h"
/* Initialise la classe des formats binaires génériques. */
static void g_binary_format_class_init(GBinFormatClass *);
/* Initialise une instance de format binaire générique. */
static void g_binary_format_init(GBinFormat *);
/* Supprime toutes les références externes. */
static void g_binary_format_dispose(GBinFormat *);
/* Procède à la libération totale de la mémoire. */
static void g_binary_format_finalize(GBinFormat *);
/* ---------------------- RASSEMBLEMENT ET GESTION DE SYMBOLES ---------------------- */
/* Retire un symbole de la collection du format binaire. */
static void _g_binary_format_remove_symbol(GBinFormat *, size_t);
/* Recherche le symbole associé à une adresse. */
static bool _g_binary_format_find_symbol(const GBinFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **);
/* Recherche un symbole particulier. */
static bool __g_binary_format_find_symbol(const GBinFormat *, const void *, __compar_fn_t, size_t *, GBinSymbol **);
/* Indique le type défini pour un format binaire générique. */
G_DEFINE_TYPE(GBinFormat, g_binary_format, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des formats binaires génériques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_class_init(GBinFormatClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_format_dispose;
object->finalize = (GObjectFinalizeFunc)g_binary_format_finalize;
g_signal_new("symbol-added",
G_TYPE_BIN_FORMAT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinFormatClass, symbol_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
g_signal_new("symbol-removed",
G_TYPE_BIN_FORMAT,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBinFormatClass, symbol_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
}
/******************************************************************************
* *
* Paramètres : format = instance à initialiser. *
* *
* Description : Initialise une instance de format binaire générique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_init(GBinFormat *format)
{
INIT_BIN_FORMAT_EXTRA(format);
g_rw_lock_init(&format->pt_lock);
format->info = g_preload_info_new();
format->demangler = NULL;
g_rw_lock_init(&format->syms_lock);
#ifndef NDEBUG
g_atomic_int_set(&format->sym_locked, 0);
#endif
format->errors = NULL;
format->error_count = 0;
g_mutex_init(&format->error_mutex);
#ifndef NDEBUG
g_atomic_int_set(&format->error_locked, 0);
#endif
}
/******************************************************************************
* *
* Paramètres : format = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_dispose(GBinFormat *format)
{
size_t i; /* Boucle de parcours */
g_clear_object(&format->content);
g_rw_lock_clear(&format->pt_lock);
g_clear_object(&format->info);
g_clear_object(&format->demangler);
for (i = 0; i < format->sym_count; i++)
g_clear_object(&format->symbols[i]);
g_rw_lock_clear(&format->syms_lock);
g_mutex_clear(&format->error_mutex);
G_OBJECT_CLASS(g_binary_format_parent_class)->dispose(G_OBJECT(format));
}
/******************************************************************************
* *
* Paramètres : format = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_format_finalize(GBinFormat *format)
{
DisassPriorityLevel i; /* Boucle de parcours #1 */
size_t k; /* Boucle de parcours #2 */
for (i = 0; i < DPL_COUNT; i++)
if (format->start_points[i] != NULL)
free(format->start_points[i]);
if (format->symbols != NULL)
free(format->symbols);
if (format->errors != NULL)
{
for (k = 0; k < format->error_count; k++)
if (format->errors[k].desc != NULL)
free(format->errors[k].desc);
free(format->errors);
}
G_OBJECT_CLASS(g_binary_format_parent_class)->finalize(G_OBJECT(format));
}
/******************************************************************************
* *
* Paramètres : format = instance à traiter. *
* status = barre de statut à tenir informée. *
* *
* Description : Effectue les ultimes opérations de chargement d'un binaire. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_complete_loading(GBinFormat *format, GtkStatusStack *status)
{
return true;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* content = contenu binaire à parcourir. *
* *
* Description : Définit le contenu binaire à analyser. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_set_content(GBinFormat *format, GBinContent *content)
{
assert(format->content == NULL);
g_object_ref(G_OBJECT(content));
format->content = content;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Fournit une référence vers le contenu binaire analysé. *
* *
* Retour : Gestionnaire de contenu binaire en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinContent *g_binary_format_get_content(const GBinFormat *format)
{
GBinContent *result; /* Instance à retourner */
result = format->content;
g_object_ref(G_OBJECT(result));
return result;
}
/******************************************************************************
* *
* Paramètres : format = format à venir modifier. *
* flag = drapeau d'information complémentaire à planter. *
* *
* Description : Ajoute une information complémentaire à un format. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_set_flag(GBinFormat *format, FormatFlag flag)
{
bool result; /* Bilan à retourner */
fmt_obj_extra *extra; /* Données insérées à modifier */
extra = GET_BIN_FORMAT_EXTRA(format);
g_bit_lock(&extra->lock, HOLE_LOCK_BIT);
result = !(extra->flags & flag);
extra->flags |= flag;
g_bit_unlock(&extra->lock, HOLE_LOCK_BIT);
return result;
}
/******************************************************************************
* *
* Paramètres : format = format à venir modifier. *
* flag = drapeau d'information complémentaire à planter. *
* *
* Description : Retire une information complémentaire à un format. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_unset_flag(GBinFormat *format, FormatFlag flag)
{
bool result; /* Bilan à retourner */
fmt_obj_extra *extra; /* Données insérées à modifier */
extra = GET_BIN_FORMAT_EXTRA(format);
g_bit_lock(&extra->lock, HOLE_LOCK_BIT);
result = (extra->flags & flag);
extra->flags &= ~flag;
g_bit_unlock(&extra->lock, HOLE_LOCK_BIT);
return result;
}
/******************************************************************************
* *
* Paramètres : format = format à venir consulter. *
* flag = drapeau d'information à rechercher. *
* *
* Description : Détermine si un format possède un fanion particulier. *
* *
* Retour : Bilan de la détection. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_has_flag(const GBinFormat *format, FormatFlag flag)
{
bool result; /* Bilan à retourner */
fmt_obj_extra *extra; /* Données insérées à modifier */
extra = GET_BIN_FORMAT_EXTRA(format);
g_bit_lock(&extra->lock, HOLE_LOCK_BIT);
result = (extra->flags & flag);
g_bit_unlock(&extra->lock, HOLE_LOCK_BIT);
return result;
}
/******************************************************************************
* *
* Paramètres : format = format à venir consulter. *
* *
* Description : Fournit les particularités du format. *
* *
* Retour : Somme de tous les fanions associés au format. *
* *
* Remarques : - *
* *
******************************************************************************/
FormatFlag g_binary_format_get_flags(const GBinFormat *format)
{
FormatFlag result; /* Fanions à retourner */
fmt_obj_extra *extra; /* Données insérées à modifier */
extra = GET_BIN_FORMAT_EXTRA(format);
g_bit_lock(&extra->lock, HOLE_LOCK_BIT);
result = (extra->flags & FFL_MASK);
g_bit_unlock(&extra->lock, HOLE_LOCK_BIT);
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Indique la désignation interne du format. *
* *
* Retour : Description du format. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_binary_format_get_name(const GBinFormat *format)
{
const char *result; /* Désignation à retourner */
result = G_BIN_FORMAT_GET_CLASS(format)->get_name(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Fournit une description humaine du format. *
* *
* Retour : Description du format. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_binary_format_get_description(const GBinFormat *format)
{
const char *result; /* Désignation à retourner */
result = G_BIN_FORMAT_GET_CLASS(format)->get_desc(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = format chargé dont l'analyse est lancée. *
* gid = groupe de travail dédié. *
* status = barre de statut à tenir informée. *
* *
* Description : Assure l'interprétation d'un format en différé. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_analyze(GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status)
{
bool result; /* Bilan à retourner */
GBinFormatClass *class; /* Classe de l'instance */
handle_binary_format_analysis(PGA_FORMAT_ANALYSIS_STARTED, format, gid, status);
class = G_BIN_FORMAT_GET_CLASS(format);
result = class->analyze(format, gid, status);
handle_binary_format_analysis(PGA_FORMAT_ANALYSIS_ENDED, format, gid, status);
return result;
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* *
* Description : Indique le boutisme employé par le format binaire analysé. *
* *
* Retour : Boutisme associé au format. *
* *
* Remarques : - *
* *
******************************************************************************/
SourceEndian g_binary_format_get_endianness(const GBinFormat *format)
{
return G_BIN_FORMAT_GET_CLASS(format)->get_endian(format);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à compléter. *
* pt = point de l'espace mémoire à considérer. *
* level = indication de priorité et d'origine de l'adresse. *
* *
* Description : Enregistre une adresse comme début d'une zone de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_register_code_point(GBinFormat *format, virt_t pt, DisassPriorityLevel level)
{
assert(level < DPL_COUNT);
g_rw_lock_writer_lock(&format->pt_lock);
if (format->pt_count[level] == format->pt_allocated[level])
{
format->pt_allocated[level] += EXTRA_POINT_BLOCK;
format->start_points[level] = realloc(format->start_points[level],
format->pt_allocated[level] * sizeof(virt_t));
}
format->start_points[level][format->pt_count[level]++] = pt;
g_rw_lock_writer_unlock(&format->pt_lock);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* ctx = contexte de désassemblage à préparer. *
* status = barre de statut à tenir informée. *
* *
* Description : Intègre dans un contexte les informations tirées d'un format.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_preload_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status)
{
g_preload_info_copy(format->info, G_PRELOAD_INFO(ctx));
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
* ctx = contexte de désassemblage à préparer. *
* status = barre de statut à tenir informée. *
* *
* Description : Définit les points de départ d'un contexte de désassemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_activate_disassembling_context(GBinFormat *format, GProcContext *ctx, GtkStatusStack *status)
{
DisassPriorityLevel i; /* Boucle de parcours #1 */
size_t k; /* Boucle de parcours #2 */
g_rw_lock_reader_lock(&format->pt_lock);
for (i = 0; i < DPL_COUNT; i++)
for (k = 0; k < format->pt_count[i]; k++)
g_proc_context_push_drop_point(ctx, i, format->start_points[i][k]);
g_rw_lock_reader_unlock(&format->pt_lock);
}
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à manipuler. *
* gid = groupe de travail dédié. *
* status = barre de statut à tenir informée. *
* *
* Description : Réalise un traitement post-désassemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_complete_analysis(GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status)
{
GBinFormatClass *class; /* Classe de l'instance */
handle_binary_format_analysis(PGA_FORMAT_POST_ANALYSIS_STARTED, format, gid, status);
class = G_BIN_FORMAT_GET_CLASS(format);
if (class->complete != NULL)
class->complete(format, gid, status);
handle_binary_format_analysis(PGA_FORMAT_POST_ANALYSIS_ENDED, format, gid, status);
}
/* ---------------------------------------------------------------------------------- */
/* DECODAGE DE SYMBOLES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = format binaire à consulter pour l'opération. *
* *
* Description : Fournit le décodeur de symboles privilégié pour un format. *
* *
* Retour : Décodeur préféré ou NULL s'il n'est pas renseigné. *
* *
* Remarques : - *
* *
******************************************************************************/
GCompDemangler *g_binary_format_get_demangler(const GBinFormat *format)
{
GCompDemangler *result; /* Décodeur à retourner */
result = format->demangler;
if (result != NULL)
g_object_ref(G_OBJECT(result));
return result;
}
/******************************************************************************
* *
* Paramètres : format = format binaire à consulter pour l'opération. *
* desc = chaîne de caractères à décoder. *
* *
* Description : Décode une chaîne de caractères donnée en type. *
* *
* Retour : Instance obtenue ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GDataType *g_binary_format_decode_type(const GBinFormat *format, const char *desc)
{
GDataType *result; /* Construction à remonter */
GCompDemangler *demangler; /* Accès plus lisible */
demangler = format->demangler;
if (demangler != NULL)
result = g_compiler_demangler_decode_type(demangler, desc);
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : format = format binaire à consulter pour l'opération. *
* desc = chaîne de caractères à décoder. *
* *
* Description : Décode une chaîne de caractères donnée en routine. *
* *
* Retour : Instance obtenue ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinRoutine *g_binary_format_decode_routine(const GBinFormat *format, const char *desc)
{
GBinRoutine *result; /* Construction à remonter */
GCompDemangler *demangler; /* Accès plus lisible */
demangler = format->demangler;
if (demangler != NULL)
result = g_compiler_demangler_decode_routine(demangler, desc);
else
result = NULL;
if (result == NULL)
{
result = g_binary_routine_new();
g_binary_routine_set_name(result, strdup(desc));
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* RASSEMBLEMENT ET GESTION DE SYMBOLES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = architecture à manipuler. *
* state = nouvel état de l'accès aux symboles. *
* *
* Description : Protège ou lève la protection de l'accès aux symboles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_lock_unlock_symbols_rd(GBinFormat *format, bool state)
{
#ifndef NDEBUG
gint test; /* Test de valeur courante */
#endif
if (state)
{
g_rw_lock_reader_lock(&format->syms_lock);
#ifndef NDEBUG
g_atomic_int_inc(&format->sym_locked);
#endif
}
else
{
#ifndef NDEBUG
test = g_atomic_int_add(&format->sym_locked, -1);
assert(test > 0);
#endif
g_rw_lock_reader_unlock(&format->syms_lock);
}
}
/******************************************************************************
* *
* Paramètres : format = architecture à manipuler. *
* state = nouvel état de l'accès aux symboles. *
* *
* Description : Protège ou lève la protection de l'accès aux symboles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_lock_unlock_symbols_wr(GBinFormat *format, bool state)
{
if (state)
{
g_rw_lock_writer_lock(&format->syms_lock);
#ifndef NDEBUG
g_atomic_int_set(&format->sym_locked, 1);
#endif
}
else
{
#ifndef NDEBUG
g_atomic_int_set(&format->sym_locked, 0);
#endif
g_rw_lock_writer_unlock(&format->syms_lock);
}
}
/******************************************************************************
* *
* Paramètres : format = architecture à consulter via la procédure. *
* *
* Description : Assure qu'un verrou est bien posé pour l'accès aux symboles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
#ifndef NDEBUG
void g_binary_format_check_for_symbols_lock(const GBinFormat *format)
{
assert(g_atomic_int_get(&format->sym_locked) > 0);
}
#endif
/******************************************************************************
* *
* Paramètres : format = architecture à consulter via la procédure. *
* *
* Description : Fournit la marque de dernière modification des symboles. *
* *
* Retour : Marque de la dernière modification de la liste de symboles. *
* *
* Remarques : - *
* *
******************************************************************************/
unsigned int g_binary_format_get_symbols_stamp(const GBinFormat *format)
{
return format->sym_stamp;
}
/******************************************************************************
* *
* Paramètres : format = format visé par la procédure. *
* *
* Description : Compte le nombre de symboles représentés. *
* *
* Retour : Nombre de symboles présents. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_binary_format_count_symbols(const GBinFormat *format)
{
assert(g_atomic_int_get(&format->sym_locked) > 0);
return format->sym_count;
}
/******************************************************************************
* *
* Paramètres : format = format visé par la procédure. *
* index = indice du symbole visé. *
* *
* Description : Fournit un symbole lié à un format. *
* *
* Retour : Symbole conservé trouvé ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinSymbol *g_binary_format_get_symbol(const GBinFormat *format, size_t index)
{
GBinSymbol *result; /* Symbole à retourner */
assert(g_atomic_int_get(&format->sym_locked) > 0);
if (format->sym_count == 0)
result = NULL;
else
{
assert(index < format->sym_count);
result = format->symbols[index];
assert(result != NULL);
g_object_ref(G_OBJECT(result));
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* symbol = symbole à ajouter à la liste. *
* *
* Description : Ajoute un symbole à la collection du format binaire. *
* *
* Retour : true si le symbole était bien localisé et a été inséré. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol)
{
bool result; /* Statut d'ajout à retourner */
#ifndef NDEBUG
const mrange_t *range; /* Couverture du symbole */
const vmpa2t *addr; /* Emplacement du symbole */
#endif
size_t index; /* Indice du point d'insertion */
/**
* Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol()
* fassent bien leur office, il faut que les symboles soient triés.
*
* Cependant, les localisations à satisfaire lors d'une recherche recontrent
* un problème si les positions physiques ne sont pas renseignées. En effet
* les adresses virtuelles en sont potentiellement décorrélées (c'est le cas
* avec le format ELF par exemple, où les zones en mémoire ne suivent pas le
* même ordre que les segments du binaire).
*
* Comme les comparaisons entre localisations se réalisent sur les éléments
* renseignés communs, à commencer par la position physique si c'est possible,
* une localisation s'appuyant uniquement sur une adresse virtuelle va être
* analysée suivant une liste non triée d'adresses virtuelles.
*
* On corrige donc le tir si besoin est en forçant la comparaison via les
* positions physiques.
*/
#ifndef NDEBUG
range = g_binary_symbol_get_range(symbol);
addr = get_mrange_addr(range);
assert(has_phys_addr(addr) || g_binary_symbol_get_status(symbol) == SSS_DYNAMIC);
#endif
g_binary_format_lock_unlock_symbols_wr(format, true);
/**
* Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution
* amènent à la création d'un même symbole.
*
* Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps)
* pour faire une vérification avant construction puis ajout, on préfère limiter
* l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la
* construction du symbole dans les cas peu fréquents où le symbole était déjà en place.
*/
result = bsearch_index(&symbol, format->symbols, format->sym_count,
sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index);
if (!result)
{
format->symbols = _qinsert(format->symbols, &format->sym_count,
sizeof(GBinSymbol *), &symbol, index);
format->sym_stamp++;
result = true;
}
else
g_object_unref(G_OBJECT(symbol));
g_binary_format_lock_unlock_symbols_wr(format, false);
if (result)
g_signal_emit_by_name(format, "symbol-added", symbol);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* symbols = ensemble de symboles à ajouter à la liste. *
* count = taille de cet ensemble. *
* *
* Description : Ajoute plusieurs symboles à la collection du format binaire. *
* *
* Retour : true si les symboles dûment localisés ont été insérés. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_t count)
{
bool result; /* Statut d'ajout à retourner */
#ifndef NDEBUG
phys_t last; /* Dernière position rencontrée*/
#endif
size_t i; /* Boucle de parcours */
#ifndef NDEBUG
const mrange_t *range; /* Couverture du symbole */
const vmpa2t *addr; /* Emplacement du symbole */
#endif
size_t index; /* Indice du point d'insertion */
/**
* Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol()
* fassent bien leur office, il faut que les symboles soient triés.
*
* Cependant, les localisations à satisfaire lors d'une recherche recontrent
* un problème si les positions physiques ne sont pas renseignées. En effet
* les adresses virtuelles en sont potentiellement décorrélées (c'est le cas
* avec le format ELF par exemple, où les zones en mémoire ne suivent pas le
* même ordre que les segments du binaire).
*
* Comme les comparaisons entre localisations se réalisent sur les éléments
* renseignés communs, à commencer par la position physique si c'est possible,
* une localisation s'appuyant uniquement sur une adresse virtuelle va être
* analysée suivant une liste non triée d'adresses virtuelles.
*
* On corrige donc le tir si besoin est en forçant la comparaison via les
* positions physiques.
*/
#ifndef NDEBUG
last = VMPA_NO_PHYSICAL;
for (i = 0; i < count; i++)
{
range = g_binary_symbol_get_range(symbols[i]);
addr = get_mrange_addr(range);
assert(has_phys_addr(addr) || g_binary_symbol_get_status(symbols[i]) == SSS_DYNAMIC);
if (has_phys_addr(addr))
{
assert(last == VMPA_NO_PHYSICAL || last <= get_phy_addr(addr));
last = get_phy_addr(addr);
}
}
#endif
g_binary_format_lock_unlock_symbols_wr(format, true);
/**
* Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution
* amènent à la création d'un même symbole.
*
* Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps)
* pour faire une vérification avant construction puis ajout, on préfère limiter
* l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la
* construction du symbole dans les cas peu fréquents où le symbole était déjà en place.
*/
result = bsearch_index(&symbols[0], format->symbols, format->sym_count,
sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index);
if (!result)
{
for (i = 0; i < count; i++)
g_object_ref(G_OBJECT(symbols[i]));
format->symbols = _qinsert_batch(format->symbols, &format->sym_count,
sizeof(GBinSymbol *), symbols, count, index);
format->sym_stamp++;
result = true;
}
g_binary_format_lock_unlock_symbols_wr(format, false);
if (result)
for (i = 0; i < count; i++)
g_signal_emit_by_name(format, "symbol-added", symbols[i]);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* index = indice du symbole à retirer de la liste. *
* *
* Description : Retire un symbole de la collection du format binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void _g_binary_format_remove_symbol(GBinFormat *format, size_t index)
{
assert(g_atomic_int_get(&format->sym_locked) == 1);
assert(index < format->sym_count);
g_object_unref(G_OBJECT(format->symbols[index]));
if ((index + 1) < format->sym_count)
memmove(&format->symbols[index], &format->symbols[index + 1],
(format->sym_count - index - 1) * sizeof(GBinSymbol *));
format->symbols = realloc(format->symbols, --format->sym_count * sizeof(GBinSymbol *));
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à compléter. *
* symbol = symbole à retirer de la liste. *
* *
* Description : Retire un symbole de la collection du format binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_format_remove_symbol(GBinFormat *format, GBinSymbol *symbol)
{
bool found; /* Jeton de présence */
size_t index; /* Indice du point de retrait */
g_object_ref(G_OBJECT(symbol));
g_binary_format_lock_unlock_symbols_wr(format, true);
found = bsearch_index(&symbol, format->symbols, format->sym_count,
sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index);
if (found)
_g_binary_format_remove_symbol(format, index);
g_binary_format_lock_unlock_symbols_wr(format, false);
if (found)
g_signal_emit_by_name(format, "symbol-removed", symbol);
g_object_unref(G_OBJECT(symbol));
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* label = étiquette à retrouver lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole correspondant à une étiquette. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_by_label(GBinFormat *format, const char *label, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
char *cur_lbl; /* Etiquette courante */
result = false;
g_binary_format_lock_symbols_rd(format);
for (i = 0; i < format->sym_count && !result; i++)
{
cur_lbl = g_binary_symbol_get_label(format->symbols[i]);
if (cur_lbl == NULL) continue;
if (strcmp(label, cur_lbl) == 0)
{
*symbol = format->symbols[i];
g_object_ref(G_OBJECT(*symbol));
result = true;
}
free(cur_lbl);
}
g_binary_format_unlock_symbols_rd(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* fn = méthode de comparaison des symboles. *
* index = indice de l'éventuel symbole trouvé ou NULL. [OUT] *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole associé à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_binary_format_find_symbol(const GBinFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol)
{
/**
* Pour ce qui est des justifications quant à la vérification suivante,
* se référer aux commentaires placés dans g_binary_format_add_symbol().
*/
assert(has_phys_addr(addr));
return __g_binary_format_find_symbol(format, addr, fn, index, symbol);
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* key = clef fournie pour distinguer les éléments. *
* fn = méthode de comparaison des symboles. *
* index = indice de l'éventuel symbole trouvé ou NULL. [OUT] *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche un symbole particulier. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool __g_binary_format_find_symbol(const GBinFormat *format, const void *key, __compar_fn_t fn, size_t *index, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
void *found; /* Résultat de recherches */
assert(g_atomic_int_get(&format->sym_locked) > 0);
found = bsearch(key, format->symbols, format->sym_count, sizeof(GBinSymbol *), fn);
if (found != NULL)
{
if (index != NULL)
*index = (GBinSymbol **)found - format->symbols;
if (symbol != NULL)
{
*symbol = *(GBinSymbol **)found;
g_object_ref(G_OBJECT(*symbol));
}
result = true;
}
else
{
if (symbol != NULL)
*symbol = NULL;
result = false;
}
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* index = indice de l'éventuel symbole trouvé. [OUT] *
* *
* Description : Recherche l'indice du symbole correspondant à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_index_at(GBinFormat *format, const vmpa2t *addr, size_t *index)
{
bool result; /* Bilan à retourner */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_vmpa(addr, get_mrange_addr(range));
}
g_binary_format_lock_symbols_rd(format);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, index, NULL);
g_binary_format_unlock_symbols_rd(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole correspondant à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_vmpa(addr, get_mrange_addr(range));
}
g_binary_format_lock_symbols_rd(format);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol);
g_binary_format_unlock_symbols_rd(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole contenant une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_symbol_for(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_mrange_with_vmpa(range, addr);
}
g_binary_format_lock_symbols_rd(format);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol);
g_binary_format_unlock_symbols_rd(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* *
* Description : Recherche le symbole suivant celui lié à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_next_symbol_at(GBinFormat *format, const vmpa2t *addr, GBinSymbol **symbol)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice à considérer */
int find_symbol(const vmpa2t *addr, const GBinSymbol **sym)
{
const mrange_t *range; /* Espace mémoire parcouru */
range = g_binary_symbol_get_range(*sym);
return cmp_mrange_with_vmpa(range, addr);
}
g_binary_format_lock_symbols_rd(format);
result = _g_binary_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL);
if (result && (index + 1) < format->sym_count)
{
*symbol = format->symbols[index + 1];
g_object_ref(G_OBJECT(*symbol));
}
else
{
*symbol = NULL;
result = false;
}
g_binary_format_unlock_symbols_rd(format);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* range = zone à cibler lors des recherches. *
* index = indice de l'éventuel symbole trouvé. [OUT] *
* *
* Description : Recherche le premier symbole inclus dans une zone mémoire. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_find_first_symbol_inside(GBinFormat *format, const mrange_t *range, size_t *index)
{
bool result; /* Bilan à retourner */
const GBinSymbol *prev; /* Symbole précédent */
const mrange_t *srange; /* Espace mémoire associé */
int ret; /* Bilan de comparaison */
int find_symbol(const mrange_t *ref_range, const GBinSymbol **sym)
{
const mrange_t *sym_range; /* Espace mémoire parcouru */
int ret;
sym_range = g_binary_symbol_get_range(*sym);
ret = cmp_mrange_with_vmpa(ref_range, get_mrange_addr(sym_range));
ret *= -1;
return ret;
}
g_rw_lock_reader_lock(&format->syms_lock);
result = __g_binary_format_find_symbol(format, range, (__compar_fn_t)find_symbol, index, NULL);
if (result)
while (*index > 0)
{
prev = format->symbols[*index - 1];
srange = g_binary_symbol_get_range(prev);
ret = cmp_mrange_with_vmpa(range, get_mrange_addr(srange));
assert(ret <= 0);
if (ret < 0) break;
else (*index)--;
}
g_rw_lock_reader_unlock(&format->syms_lock);
return result;
}
/******************************************************************************
* *
* Paramètres : format = informations chargées à consulter. *
* addr = adresse à cibler lors des recherches. *
* strict = indication de tolérance acceptée. *
* symbol = éventuel symbole trouvé à déréfenrencer. [OUT] *
* diff = décalage entre l'adresse et le symbole. [OUT] *
* *
* Description : Recherche le symbole correspondant à une adresse. *
* *
* Retour : true si l'opération a été un succès, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_binary_format_resolve_symbol(GBinFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Espace mémoire parcouru */
if (strict)
result = g_binary_format_find_symbol_at(format, addr, symbol);
else
result = g_binary_format_find_symbol_for(format, addr, symbol);
if (result)
{
range = g_binary_symbol_get_range(*symbol);
*diff = compute_vmpa_diff(get_mrange_addr(range), addr);
assert(!strict || *diff == 0);
}
else
*diff = 0;
return result;
}
/* ---------------------------------------------------------------------------------- */
/* CONSERVATION DES SOUCIS DURANT LE CHARGEMENT */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : format = 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_binary_format_lock_unlock_errors(GBinFormat *format, bool state)
{
if (state)
{
g_mutex_lock(&format->error_mutex);
#ifndef NDEBUG
g_atomic_int_set(&format->error_locked, 1);
#endif
}
else
{
#ifndef NDEBUG
g_atomic_int_set(&format->error_locked, 0);
#endif
g_mutex_unlock(&format->error_mutex);
}
}
/******************************************************************************
* *
* Paramètres : format = 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_binary_format_add_error(GBinFormat *format, BinaryFormatError type, const vmpa2t *addr, const char *desc)
{
fmt_error *error; /* Raccourci de confort */
g_binary_format_lock_errors(format);
format->errors = realloc(format->errors, ++format->error_count * sizeof(fmt_error));
error = &format->errors[format->error_count - 1];
error->type = type;
copy_vmpa(&error->addr, addr);
if (desc != NULL)
error->desc = strdup(desc);
else
error->desc = NULL;
g_binary_format_unlock_errors(format);
}
/******************************************************************************
* *
* Paramètres : format = 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_binary_format_count_errors(GBinFormat *format)
{
size_t result; /* Quantité à retourner */
assert(g_atomic_int_get(&format->error_locked) == 1);
result = format->error_count;
return result;
}
/******************************************************************************
* *
* Paramètres : format = 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_binary_format_get_error(GBinFormat *format, size_t index, BinaryFormatError *type, vmpa2t *addr, char **desc)
{
bool result; /* Bilan à retourner */
fmt_error *error; /* Raccourci de confort */
assert(g_atomic_int_get(&format->error_locked) == 1);
result = (index < format->error_count);
assert(result);
if (result)
{
error = &format->errors[index];
*type = error->type;
copy_vmpa(addr, &error->addr);
if (error->desc != NULL)
*desc = strdup(error->desc);
else
*desc = NULL;
}
return result;
}