diff options
Diffstat (limited to 'plugins/pychrysalide')
-rw-r--r-- | plugins/pychrysalide/Makefile.am | 1 | ||||
-rw-r--r-- | plugins/pychrysalide/arch/processor.c | 39 | ||||
-rw-r--r-- | plugins/pychrysalide/dt.c | 469 | ||||
-rw-r--r-- | plugins/pychrysalide/dt.h | 47 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/editem.c | 3 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/panel.c | 39 | ||||
-rw-r--r-- | plugins/pychrysalide/helpers.c | 74 | ||||
-rw-r--r-- | plugins/pychrysalide/helpers.h | 12 | ||||
-rw-r--r-- | plugins/pychrysalide/plugin.c | 929 | ||||
-rw-r--r-- | plugins/pychrysalide/pychrysa.c | 5 |
10 files changed, 652 insertions, 966 deletions
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index 3fe13af..d9194b9 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -7,7 +7,6 @@ libdir = $(pluginslibdir) pychrysalide_la_SOURCES = \ access.h access.c \ constval.h constval.c \ - dt.h dt.c \ helpers.h helpers.c \ plugin.h plugin.c \ pychrysa.h pychrysa.c \ diff --git a/plugins/pychrysalide/arch/processor.c b/plugins/pychrysalide/arch/processor.c index 115f980..4feaa8b 100644 --- a/plugins/pychrysalide/arch/processor.c +++ b/plugins/pychrysalide/arch/processor.c @@ -31,6 +31,7 @@ #include <i18n.h> #include <arch/processor-int.h> +#include <plugins/dt.h> #include "context.h" @@ -38,7 +39,6 @@ #include "instruction.h" #include "vmpa.h" #include "../access.h" -#include "../dt.h" #include "../helpers.h" #include "../analysis/content.h" #include "../format/executable.h" @@ -140,18 +140,16 @@ static bool define_python_arch_processor_constants(PyTypeObject *); static PyObject *py_arch_processor_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ - bool abstract; /* Validation du type parent */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ GType gtype; /* Nouveau type de processeur */ - PyObject *sys_mod_dict; /* Dictionnaire des modules */ - PyObject *modname; /* Nom du module du type */ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire dudit module */ + bool status; /* Bilan d'un enregistrement */ /* Validations diverses */ - abstract = (type == get_python_arch_processor_type()); + base = get_python_arch_processor_type(); - if (abstract) + if (type == base) { result = NULL; PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); @@ -160,30 +158,23 @@ static PyObject *py_arch_processor_new(PyTypeObject *type, PyObject *args, PyObj /* Mise en place d'un type dédié */ - gtype = built_dynamic_type(G_TYPE_ARCH_PROCESSOR, type->tp_name, - (GClassInitFunc)py_arch_processor_init_gclass); - - /* Enregistrement du nouveau GType dans Python */ - - sys_mod_dict = PyImport_GetModuleDict(); + first_time = (g_type_from_name(type->tp_name) == 0); - modname = PyDict_GetItemString(type->tp_dict, "__module__"); - - module = PyObject_GetItem(sys_mod_dict, modname); + gtype = built_dynamic_type(G_TYPE_ARCH_PROCESSOR, type->tp_name, + (GClassInitFunc)py_arch_processor_init_gclass, NULL); - dict = PyModule_GetDict(module); + if (first_time) + status = register_class_for_dynamic_pygobject(gtype, type, base); + else + status = true; - if (!_register_class_for_pygobject(dict, gtype, type, - &PyGObject_Type, get_python_arch_processor_type(), NULL)) + if (!status) { result = NULL; goto exit; } - Py_DECREF(module); - Py_DECREF(modname); - - /* On créé, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ result = PyType_GenericNew(type, args, kwds); diff --git a/plugins/pychrysalide/dt.c b/plugins/pychrysalide/dt.c deleted file mode 100644 index 0b46b45..0000000 --- a/plugins/pychrysalide/dt.c +++ /dev/null @@ -1,469 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * dt.c - possibilité de créer de nouveaux types de façon dynamique - * - * Copyright (C) 2018 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 <http://www.gnu.org/licenses/>. - */ - - -#include "dt.h" - - -#include <assert.h> -#include <malloc.h> - - - -/* ------------------------- MODULE DE GESTION DES NOUVEAUX ------------------------- */ - - -#define G_TYPE_DYNAMIC_TYPES g_dynamic_types_get_type() -#define G_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypes)) -#define G_IS_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) -#define G_IS_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) - - -/* Mémorisation des caractéristiques de type */ -typedef struct _type_dyn_info_t -{ - GType type; /* Identifiant unique obtenu */ - GClassInitFunc init; /* Définition des méthodes */ - -} type_dyn_info_t; - -/* Description de fichier binaire (instance) */ -typedef struct _GDynamicTypes -{ - GObject parent; /* A laisser en premier */ - - type_dyn_info_t **info; /* Liste d'informations utiles */ - size_t count; /* Taille de cette liste */ - -} GDynamicTypes; - -/* Description de fichier binaire (classe) */ -typedef struct _GDynamicTypesClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GDynamicTypesClass; - - -/* Indique le type défini pour une gestion de types dynamique. */ -static GType g_dynamic_types_get_type(void); - -/* Initialise la classe de gestion de types dynamique. */ -static void g_dynamic_types_class_init(GDynamicTypesClass *); - -/* Initialise une gestion de types dynamique. */ -static void g_dynamic_types_init(GDynamicTypes *); - -/* Procède à l'initialisation de l'interface de typage nouveau. */ -static void g_dynamic_types_interface_init(GTypePluginClass *); - -/* Supprime toutes les références externes. */ -static void g_dynamic_types_dispose(GDynamicTypes *); - -/* Procède à la libération totale de la mémoire. */ -static void g_dynamic_types_finalize(GDynamicTypes *); - -/* Crée un nouveau gestionnaire de nouveaux types. */ -static GDynamicTypes *g_dynamic_types_new(void); - -/* Marque une augmentation des utilisations. */ -static void g_dynamic_types_use(GDynamicTypes *); - -/* Marque une diminution des utilisations. */ -static void g_dynamic_types_unuse(GDynamicTypes *); - -/* Complète la définition d'un type dynamiquement. */ -static void g_dynamic_types_complete_type(GDynamicTypes *, GType, GTypeInfo *, GTypeValueTable *); - -/* Retrouve les informations concernant un type dynamique. */ -static const type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *, GType); - -/* Fournit un identifiant GLib pour un nouveau type Python. */ -static GType g_dynamic_types_register_type(GDynamicTypes *, GType, const char *, GClassInitFunc); - - - -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ - - -/* Encadrement des nouveaux types Python dérivés */ -static GDynamicTypes *_python_dtypes = NULL; - - - -/* ---------------------------------------------------------------------------------- */ -/* MODULE DE GESTION DES NOUVEAUX */ -/* ---------------------------------------------------------------------------------- */ - -/* Indique le type défini pour une gestion de types dynamique. */ -G_DEFINE_TYPE_WITH_CODE(GDynamicTypes, g_dynamic_types, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_TYPE_PLUGIN, g_dynamic_types_interface_init)); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe de gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_class_init(GDynamicTypesClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_dynamic_types_dispose; - object->finalize = (GObjectFinalizeFunc)g_dynamic_types_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance à initialiser. * -* * -* Description : Initialise une gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_init(GDynamicTypes *types) -{ - -} - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de typage nouveau. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_interface_init(GTypePluginClass *iface) -{ - iface->use_plugin = (GTypePluginUse)g_dynamic_types_use; - iface->unuse_plugin = (GTypePluginUnuse)g_dynamic_types_unuse; - iface->complete_type_info = (GTypePluginCompleteTypeInfo)g_dynamic_types_complete_type; - -} - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_dispose(GDynamicTypes *types) -{ - G_OBJECT_CLASS(g_dynamic_types_parent_class)->dispose(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_finalize(GDynamicTypes *types) -{ - G_OBJECT_CLASS(g_dynamic_types_parent_class)->finalize(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un nouveau gestionnaire de nouveaux types. * -* * -* Retour : Instance mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GDynamicTypes *g_dynamic_types_new(void) -{ - GDynamicTypes *result; /* Adresse à retourner */ - - result = g_object_new(G_TYPE_DYNAMIC_TYPES, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une augmentation des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_use(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une diminution des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_unuse(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* type = nouveau type GLib à traiter. * -* info = information concernant ce type à constituer. [OUT] * -* table = table de valeur à éventuellement initialiser. [OUT] * -* * -* Description : Complète la définition d'un type dynamiquement. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_complete_type(GDynamicTypes *types, GType type, GTypeInfo *info, GTypeValueTable *table) -{ - const type_dyn_info_t *nfo; /* Source d'inspiration */ - GType parent; /* Type parent du type */ - GTypeQuery query; /* Informations complémentaires*/ - - /* Consultation */ - - nfo = g_dynamic_types_find(types, type); - assert(nfo != NULL); - - parent = g_type_parent(type); - g_type_query(parent, &query); - - /* Définition */ - - info->class_size = query.class_size; - info->class_init = nfo->init; - - info->instance_size = query.instance_size; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* type = identifiant du type GLib à considérer. * -* * -* Description : Retrouve les informations concernant un type dynamique. * -* * -* Retour : Structure contenant les informations associées au type. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static const type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *types, GType target) -{ - type_dyn_info_t *result; /* Informations à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - for (i = 0; i < types->count && result == NULL; i++) - if (types->info[i]->type == target) - result = types->info[i]; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* init = procédure d'initialisation de la classe associée. * -* * -* Description : Fournit un identifiant GLib pour un nouveau type Python. * -* * -* Retour : identifiant d'un nouveau type valide, ou 0. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GType g_dynamic_types_register_type(GDynamicTypes *types, GType parent, const char *name, GClassInitFunc init) -{ - GType result; /* Identifiant à retourner */ - type_dyn_info_t *new; /* Mémorisation de paramètres */ - - /* Création d'un nouveau type adapté */ - - result = g_type_register_dynamic(parent, name, G_TYPE_PLUGIN(types), 0); - - if (result == 0) - goto exit; - - new = malloc(sizeof(type_dyn_info_t)); - - new->type = result; - new->init = init; - - /* Inscription définitive */ - - types->info = realloc(types->info, ++types->count * sizeof(type_dyn_info_t *)); - - types->info[types->count - 1] = new; - - exit: - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* ACCOMPAGNEMENTS DES NOUVEAUX TYPES */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Lance le support de dérivations de types dans Python. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool init_dynamic_python_types(void) -{ - bool result; /* Bilan à retourner */ - - _python_dtypes = g_dynamic_types_new(); - - result = (_python_dtypes != NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Arrête le support de dérivations de types dans Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void exit_dynamic_python_types(void) -{ - g_object_unref(G_OBJECT(_python_dtypes)); - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* init = procédure d'initialisation de la classe associée. * -* * -* Retour : Identifiant d'un nouveau type valide, ou 0. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -GType built_dynamic_type(GType parent, const char *name, GClassInitFunc init) -{ - GType result; /* Identifiant à retourner */ - - result = g_type_from_name(name); - - if (result == 0) - result = g_dynamic_types_register_type(_python_dtypes, parent, name, init); - - return result; - -} diff --git a/plugins/pychrysalide/dt.h b/plugins/pychrysalide/dt.h deleted file mode 100644 index b0f16db..0000000 --- a/plugins/pychrysalide/dt.h +++ /dev/null @@ -1,47 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * dt.h - prototypes pour la possibilité de créer de nouveaux types de façon dynamique - * - * Copyright (C) 2018 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 <http://www.gnu.org/licenses/>. - */ - - -#ifndef _PLUGINS_PYCHRYSALIDE_DT_H -#define _PLUGINS_PYCHRYSALIDE_DT_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ - - -/* Lance le support de dérivations de types dans Python. */ -bool init_dynamic_python_types(void); - -/* Arrête le support de dérivations de types dans Python. */ -void exit_dynamic_python_types(void); - -/* Fournit un identifiant GLib pour un nouveau type Python. */ -GType built_dynamic_type(GType, const char *, GClassInitFunc); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_DT_H */ diff --git a/plugins/pychrysalide/gui/editem.c b/plugins/pychrysalide/gui/editem.c index 986d1cb..68962ae 100644 --- a/plugins/pychrysalide/gui/editem.c +++ b/plugins/pychrysalide/gui/editem.c @@ -45,9 +45,6 @@ /* Réagit à un changement de contenu chargé en cours d'analyse. */ static void py_editor_item_change_content_wrapper(GEditorItem *, GLoadedContent *, GLoadedContent *); -/* Réagit à un changement de contenu chargé en cours d'analyse. */ -static void py_editor_item_change_content_wrapper(GEditorItem *, GLoadedContent *, GLoadedContent *); - /* Réagit à un changement de vue du contenu en cours d'analyse. */ static void py_editor_item_change_view_wrapper(GEditorItem *, GLoadedPanel *, GLoadedPanel *); diff --git a/plugins/pychrysalide/gui/panels/panel.c b/plugins/pychrysalide/gui/panels/panel.c index 1df0670..d4062d3 100644 --- a/plugins/pychrysalide/gui/panels/panel.c +++ b/plugins/pychrysalide/gui/panels/panel.c @@ -32,11 +32,11 @@ #include <core/params.h> #include <gui/core/panels.h> #include <gui/panels/panel-int.h> +#include <plugins/dt.h> #include "../editem.h" #include "../../access.h" -#include "../../dt.h" #include "../../helpers.h" #include "../../gtkext/dockable.h" @@ -89,18 +89,16 @@ static bool py_panel_item_define_constants(PyTypeObject *); static PyObject *py_panel_item_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ - bool abstract; /* Validation du type parent */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ GType gtype; /* Nouveau type de processeur */ - PyObject *sys_mod_dict; /* Dictionnaire des modules */ - PyObject *modname; /* Nom du module du type */ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire dudit module */ + bool status; /* Bilan d'un enregistrement */ /* Validations diverses */ - abstract = (type == get_python_panel_item_type()); + base = get_python_panel_item_type(); - if (abstract) + if (type == base) { result = NULL; PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); @@ -109,30 +107,23 @@ static PyObject *py_panel_item_new(PyTypeObject *type, PyObject *args, PyObject /* Mise en place d'un type dédié */ - gtype = built_dynamic_type(G_TYPE_PANEL_ITEM, type->tp_name, - (GClassInitFunc)py_panel_item_init_gclass); - - /* Enregistrement du nouveau GType dans Python */ - - sys_mod_dict = PyImport_GetModuleDict(); + first_time = (g_type_from_name(type->tp_name) == 0); - modname = PyDict_GetItemString(type->tp_dict, "__module__"); - - module = PyObject_GetItem(sys_mod_dict, modname); + gtype = built_dynamic_type(G_TYPE_PANEL_ITEM, type->tp_name, + (GClassInitFunc)py_panel_item_init_gclass, NULL); - dict = PyModule_GetDict(module); + if (first_time) + status = register_class_for_dynamic_pygobject(gtype, type, base); + else + status = true; - if (!_register_class_for_pygobject(dict, gtype, type, - &PyGObject_Type, get_python_panel_item_type(), NULL)) + if (!status) { result = NULL; goto exit; } - Py_DECREF(module); - Py_DECREF(modname); - - /* On créé, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ result = PyType_GenericNew(type, args, kwds); diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index ad62de4..a930097 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -701,3 +701,77 @@ bool _register_class_for_pygobject(PyObject *dict, GType gtype, PyTypeObject *ty return result; } + + +/****************************************************************************** +* * +* Paramètres : dict = dictionnaire où conserver une référence au type créé.* +* gtype = type dans sa version GLib. * +* type = type dans sa version Python. * +* base = type de base de l'objet. * +* * +* Description : Enregistre un type Python dérivant d'un type GLib dynamique. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTypeObject *base) +{ + bool result; /* Bilan à retourner */ + PyTypeObject *legacy_parent; /* Type parent d'origine */ + PyObject *sys_mod_dict; /* Dictionnaire des modules */ + PyObject *modname; /* Nom du module du type */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire dudit module */ + + /** + * Lors de l'appel à pygobject_register_class(), PyGObject remplace systématiquement + * le type Python fourni par : + * + * Py_TYPE(type) = PyGObject_MetaType; + * + * Ce nouveau type est le suivant (cf. gi/types.py) : + * + * class _GObjectMetaBase(type): + * """Metaclass for automatically registering GObject classes.""" + * + * D'une part, comme les enregistrements sont gérés manuellement dans le cas de + * types dérivés dynamiquement d'objets natifs, ce changement est inutile. + * + * D'autre part, il semble avoir un soucis de références (cf. testGarbageCollecting() + * du fichier de test plugins/plugin.py) : + * + * python3dm: ../Modules/gcmodule.c:379: visit_decref: Assertion `_PyGCHead_REFS(gc) != 0' failed. + * + * #3 __GI___assert_fail () + * #4 visit_decref () + * #5 subtype_traverse () + * #6 subtract_refs () + * #7 collect () + * #8 collect_with_callback () + * #9 gc_collect () + * + * On restaure donc le type d'origine de l'objet Python créé dynamquement pour éviter + * ce genre de soucis. + */ + + legacy_parent = Py_TYPE(type); + + sys_mod_dict = PyImport_GetModuleDict(); + + modname = PyDict_GetItemString(type->tp_dict, "__module__"); + + module = PyObject_GetItem(sys_mod_dict, modname); + + dict = PyModule_GetDict(module); + + result = _register_class_for_pygobject(dict, gtype, type, &PyGObject_Type, base, NULL); + + Py_TYPE(type) = legacy_parent; + + return result; + +} diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index e080ca4..bf95e61 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -25,8 +25,9 @@ #define _PLUGINS_PYCHRYSALIDE_HELPERS_H -#include <glib-object.h> #include <Python.h> +#include <assert.h> +#include <glib-object.h> #include <stdbool.h> @@ -97,13 +98,8 @@ bool _register_class_for_pygobject(PyObject *, GType, PyTypeObject *, PyTypeObje #define register_class_for_pygobject(dict, gtype, type, base) \ _register_class_for_pygobject(dict, gtype, type, base, NULL) - -/** - * Quand on remplace un objet GLib dans le dos de Python, il faut - * le remplacer de la même manière qu'on l'a obtenu ! - */ - -#define pygobject_set(p, v) ((PyGObject *)(p))->obj = (GObject *)v +/* Enregistre un type Python dérivant d'un type GLib dynamique. */ +bool register_class_for_dynamic_pygobject(GType, PyTypeObject *, PyTypeObject *); diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c index a693d7b..22bfabb 100644 --- a/plugins/pychrysalide/plugin.c +++ b/plugins/pychrysalide/plugin.c @@ -32,6 +32,7 @@ #include <common/extstr.h> +#include <plugins/dt.h> #include <plugins/plugin-int.h> @@ -41,6 +42,50 @@ +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Accompagne la création d'une instance dérivée en Python. */ +static PyObject *py_plugin_module_new(PyTypeObject *, PyObject *, PyObject *); + +/* Initialise la classe des greffons d'extension. */ +static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); + +/* Valide les fonctionnalités déclarées en actions. */ +static bool py_plugin_module_check_interface(PyObject *); + +/* Accompagne la fin du chargement des modules natifs. */ +static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *, PluginAction); + +/* Complète une liste de resources pour thème. */ +static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, char ***, size_t *); + +/* Procède à une opération liée à un contenu binaire. */ +static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); + +/* Procède à une opération liée à un contenu chargé. */ +static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *); + +/* Procède à une opération liée à l'analyse d'un format. */ +static bool py_plugin_module_handle_binary_format_analysis_wrapper(const GPluginModule *, PluginAction, GBinFormat *, wgroup_id_t, GtkStatusStack *); + +/* Procède à un préchargement de format de fichier. */ +static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); + +/* Procède au rattachement d'éventuelles infos de débogage. */ +static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *); + +/* Exécute une action pendant un désassemblage de binaire. */ +static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); + +/* Effectue la détection d'effets d'outils externes. */ +static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *); + + + /* --------------------- INTERFACE INTERNE POUR GREFFONS PYTHON --------------------- */ @@ -75,30 +120,6 @@ static void g_python_plugin_dispose(GPythonPlugin *); /* Description : Procède à la libération totale de la mémoire. */ static void g_python_plugin_finalize(GPythonPlugin *); -/* Reconstruit la déclaration d'interface à partir de lectures. */ -static bool g_python_plugin_read_interface(GPythonPlugin *); - -/* Procède à l'initialisation du greffon. */ -static bool g_python_plugin_do_init(GPythonPlugin *); - -/* Procède à l'extinction du greffon. */ -static bool g_python_plugin_do_exit(GPythonPlugin *); - -/* Procède à une opération liée à un contenu binaire. */ -static void g_python_plugin_handle_binary_content(const GPythonPlugin *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à l'analyse d'un format. */ -static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *, PluginAction, GBinFormat *, wgroup_id_t, GtkStatusStack *); - -/* Procède à un préchargement de format de fichier. */ -static bool g_python_plugin_preload_binary_format(const GPythonPlugin *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); - -/* Procède au rattachement d'éventuelles infos de débogage. */ -static void g_python_plugin_attach_debug_format(const GPythonPlugin *, PluginAction, GExeFormat *); - -/* Exécute une action pendant un désassemblage de binaire. */ -static void g_python_plugin_process_disass(const GPythonPlugin *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); - /* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ @@ -113,61 +134,78 @@ static bool py_plugin_module_define_constants(PyTypeObject *); /* ---------------------------------------------------------------------------------- */ -/* INTERFACE INTERNE POUR GREFFONS PYTHON */ +/* GLUE POUR CREATION DEPUIS PYTHON */ /* ---------------------------------------------------------------------------------- */ -/* Indique le type défini par la GLib pour le greffon Python. */ -G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); - - /****************************************************************************** * * -* Paramètres : klass = classe à initialiser. * +* Paramètres : type = type du nouvel objet à mettre en place. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition. * * * -* Description : Initialise la classe des greffons Python. * +* Description : Accompagne la création d'une instance dérivée en Python. * * * -* Retour : - * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ -static void g_python_plugin_class_init(GPythonPluginClass *klass) +static PyObject *py_plugin_module_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - GObjectClass *object; /* Autre version de la classe */ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ - object = G_OBJECT_CLASS(klass); + /* Validations diverses */ - object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; - object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + base = get_python_plugin_module_type(); -} + if (type == base) + { + result = NULL; + PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); + goto exit; + } + /* Mise en place d'un type dédié */ -/****************************************************************************** -* * -* Paramètres : plugin = instance à initialiser. * -* * -* Description : Initialise l'instance d'un greffon Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + first_time = (g_type_from_name(type->tp_name) == 0); -static void g_python_plugin_init(GPythonPlugin *plugin) -{ + gtype = built_dynamic_type(G_TYPE_PYTHON_PLUGIN, type->tp_name, + (GClassInitFunc)py_plugin_module_init_gclass, NULL); + + if (first_time) + status = register_class_for_dynamic_pygobject(gtype, type, base); + else + status = true; + + if (!status) + { + result = NULL; + goto exit; + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; } /****************************************************************************** * * -* Paramètres : plugin = instance d'objet GLib à traiter. * +* Paramètres : class = classe à initialiser. * +* unused = données non utilisées ici. * * * -* Description : Supprime toutes les références externes. * +* Description : Initialise la classe des greffons d'extension. * * * * Retour : - * * * @@ -175,191 +213,147 @@ static void g_python_plugin_init(GPythonPlugin *plugin) * * ******************************************************************************/ -static void g_python_plugin_dispose(GPythonPlugin *plugin) +static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) { - PyThreadState *tstate; /* Contexte d'environnement */ + class->init = NULL; + class->exit = NULL; - /** - * Cf. https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock - * - * Cependant, comme on se trouve à priori dans le thread principal de l'interpréteur, - * PyGILState_Ensure() ne pose aucun verrou. Ce qui aboutit à la situation suivante : - * - * Fatal Python error: drop_gil: GIL is not locked - * - * On peut forcer les choses avec PyEval_AcquireLock(), mais cette fonction est marquée - * comme dépréciée depuis Python 3.2. - * - * Donc on choisit les alternatives officielles. - * - * Cependant, PyThreadState_Get() renvoit l'erreur suivante : - * - * Fatal Python error: PyThreadState_Get: no current thread - * - * Donc on se rabat sur une sauvegarde, qui n'est initialisée que lorsque l'interpréteur - * est intégré dans l'éditeur. - */ + class->native_loaded = py_plugin_module_notify_native_loaded_wrapper; - tstate = get_pychrysalide_main_tstate(); + class->include_theme = py_plugin_module_include_theme_wrapper; - if (tstate != NULL) - PyEval_RestoreThread(tstate); + class->handle_content = py_plugin_module_handle_binary_content_wrapper; + class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - Py_XDECREF(plugin->instance); - plugin->instance = NULL; + class->handle_fmt_analysis = py_plugin_module_handle_binary_format_analysis_wrapper; + class->preload_format = py_plugin_module_preload_binary_format_wrapper; + class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - if (tstate != NULL) - PyEval_SaveThread(); + class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin)); + class->detect = py_plugin_module_detect_external_tools_wrapper; } /****************************************************************************** * * -* Paramètres : plugin = instance d'objet GLib à traiter. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Procède à la libération totale de la mémoire. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void g_python_plugin_finalize(GPythonPlugin *plugin) +static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) { - plugin_interface *final; /* Interface finale conservée */ + const char *name; /* Désignation humaine courte */ + const char *desc; /* Description plus loquace */ + const char *version; /* Version du greffon */ + PyObject *actions_obj; /* Liste des actions offertes */ + int ret; /* Bilan de lecture des args. */ + PyObject *new_kwds; /* Nouveau dictionnaire épuré */ + GPluginModule *plugin; /* Greffon à manipuler */ + plugin_interface *iface; /* Interface à constituer */ + size_t i; /* Boucle de parcours */ + PyObject *action; /* Identifiant d'une action */ - Py_DECREF(plugin->module); + static char *kwlist[] = { "name", "desc", "version", "actions", NULL }; - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + /* Récupération des paramètres */ - if (final != NULL) - { - assert(final->required_count == 1); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "sssO!", kwlist, + &name, &desc, &version, &PyTuple_Type, &actions_obj); + if (!ret) return -1; - free(final->required); - free(final); + /* Initialisation d'un objet GLib */ - } + new_kwds = PyDict_New(); - G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + ret = PyGObject_Type.tp_init(self, args, new_kwds); -} + Py_DECREF(new_kwds); + if (ret == -1) return -1; -/****************************************************************************** -* * -* Paramètres : modname = nom du module à charger. * -* filename = chemin d'accès au code Python à charger. * -* * -* Description : Crée un greffon à partir de code Python. * -* * -* Retour : Adresse de la structure mise en place ou NULL si erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ + /* Eléments de base */ -GPluginModule *g_python_plugin_new(const char *modname, const char *filename) -{ - GPythonPlugin *result; /* Structure à retourner */ - PyObject *name; /* Chemin d'accès pour Python */ - PyObject *module; /* Script Python chargé */ - PyObject *err_type; /* Type d'erreur Python */ - PyObject *err_value; /* Instance Python d'erreur */ - PyObject *err_traceback; /* Trace Python associée */ - PyObject *err_string; /* Description Python d'erreur */ - const char *err_msg; /* Représentation humaine */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - name = PyUnicode_FromString(modname); - if (name == NULL) goto gppn_bad_exit; + iface = malloc(sizeof(plugin_interface)); + plugin->interface = iface; - module = PyImport_Import(name); - Py_DECREF(name); + iface->name = strdup(name); + iface->desc = strdup(desc); + iface->version = strdup(version); - if (PyErr_Occurred()) - { - PyErr_Fetch(&err_type, &err_value, &err_traceback); + iface->container = false; - if (err_value == NULL) - log_variadic_message(LMT_ERROR, - _("An unknown error occured when importing '%s'..."), modname); - else - { - err_string = PyObject_Str(err_value); - err_msg = PyUnicode_AsUTF8(err_string); + iface->required = malloc(sizeof(char *)); + iface->required[0] = "PyChrysalide"; + iface->required_count = 1; - log_variadic_message(LMT_ERROR, - _("An error occured when importing '%s': \"%s\""), modname, err_msg); + iface->actions_count = PyTuple_Size(actions_obj); + iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - Py_DECREF(err_string); - Py_DECREF(err_value); + for (i = 0; i < iface->actions_count; i++) + { + action = PyTuple_GetItem(actions_obj, i); + if (!PyLong_Check(action)) + { + PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); + return -1; } - Py_XDECREF(err_traceback); - Py_XDECREF(err_type); - - Py_XDECREF(module); + iface->actions[i] = PyLong_AsUnsignedLong(action); - module = NULL; + } + if (!py_plugin_module_check_interface(self)) + { + PyErr_SetString(PyExc_TypeError, _("missing features for the declared actions.")); + return -1; } - if (module == NULL) goto gppn_bad_exit; + return 0; - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); +} - if (class == NULL) goto gppn_no_class; - if (!PyType_Check(class->ob_type)) goto gppn_no_class; - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto gppn_no_instance; +/****************************************************************************** +* * +* Paramètres : self = greffon Python en cours d'initialisation. * +* * +* Description : Valide les fonctionnalités déclarées en actions. * +* * +* Retour : true si le greffon Python est à priori utilisable. * +* * +* Remarques : - * +* * +******************************************************************************/ - result = g_object_new(G_TYPE_PYTHON_PLUGIN, NULL); +static bool py_plugin_module_check_interface(PyObject *self) +{ + bool result; /* Bilan à retourner */ + GPluginModule *plugin; /* Greffon à manipuler */ + size_t i; /* Boucle de parcours */ + uint32_t action; /* Identifiant d'une action */ + uint32_t category; /* Catégorie principale */ + uint32_t sub; /* Sous-catégorie visée */ - G_PLUGIN_MODULE(result)->filename = strdup(filename); + result = true; - result->module = module; - result->instance = instance; + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - if (!g_python_plugin_read_interface(result)) - goto gppn_interface_error; - - /* Localisation des différents points d'entrée déclarés */ - -#define register_python_binding(inst, pysym, sym, binding) \ - ({ \ - bool __result; \ - if (!has_python_method(inst, #pysym)) \ - { \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - #pysym, G_PLUGIN_MODULE(result)->filename); \ - __result = false; \ - } \ - else \ - { \ - G_PLUGIN_MODULE(result)->sym = binding; \ - __result = true; \ - } \ - __result; \ - }) - - for (i = 0; i < G_PLUGIN_MODULE(result)->interface->actions_count; i++) + for (i = 0; i < plugin->interface->actions_count && result; i++) { - action = G_PLUGIN_MODULE(result)->interface->actions[i]; + action = plugin->interface->actions[i]; category = MASK_PLUGIN_CATEGORY(action); sub = MASK_PLUGIN_SUB_CATEGORY(action); @@ -373,20 +367,17 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) break; case PGA_PLUGIN_INIT: - if (!register_python_binding(instance, init, init, - (pg_management_fc)g_python_plugin_do_init)) - goto gppn_bad_plugin; + result = has_python_method(self, "init"); break; case PGA_PLUGIN_EXIT: - if (!register_python_binding(instance, exit, exit, - (pg_management_fc)g_python_plugin_do_exit)) - goto gppn_bad_plugin; + result = has_python_method(self, "exit"); break; default: log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + _("Unknown sub-category '0x%02x' in plugin '%s'..."), + sub, self->ob_type->tp_name); break; } @@ -402,16 +393,17 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) switch (action) { case PGA_CONTENT_EXPLORER: + result = has_python_method(self, "handle_binary_content"); + break; + case PGA_CONTENT_RESOLVER: - if (!register_python_binding(instance, handle_content, handle_content, - (pg_handle_content_fc)g_python_plugin_handle_binary_content)) - goto gppn_bad_plugin; + result = has_python_method(self, "handle_loaded_content"); break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), - action, filename); + action, self->ob_type->tp_name); break; } @@ -426,27 +418,21 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) case PGA_FORMAT_ANALYSIS_ENDED: case PGA_FORMAT_POST_ANALYSIS_STARTED: case PGA_FORMAT_POST_ANALYSIS_ENDED: - if (!register_python_binding(instance, handle_format_analysis, handle_fmt_analysis, - (pg_handle_format_analysis_fc)g_python_plugin_handle_binary_format_analysis)) - goto gppn_bad_plugin; + result = has_python_method(self, "handle_format_analysis"); break; case PGA_FORMAT_PRELOAD: - if (!register_python_binding(instance, preload_format, preload_format, - (pg_preload_format_fc)g_python_plugin_preload_binary_format)) - goto gppn_bad_plugin; + result = has_python_method(self, "preload_format"); break; case PGA_FORMAT_ATTACH_DEBUG: - if (!register_python_binding(instance, attach_debug_format, attach_debug, - (pg_attach_debug)g_python_plugin_attach_debug_format)) - goto gppn_bad_plugin; + result = has_python_method(self, "attach_debug_format"); break; default: log_variadic_message(LMT_WARNING, _("Unknown action '0x%02x' in plugin '%s'..."), - action, filename); + action, self->ob_type->tp_name); break; } @@ -454,14 +440,13 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) break; case DPS_DISASSEMBLY: - if (!register_python_binding(instance, process_disassembly, process_disass, - (pg_process_disassembly_fc)g_python_plugin_process_disass)) - goto gppn_bad_plugin; + result = has_python_method(self, "process_disassembly"); break; default: log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + _("Unknown sub-category '0x%02x' in plugin '%s'..."), + sub, self->ob_type->tp_name); break; } @@ -470,217 +455,102 @@ GPluginModule *g_python_plugin_new(const char *modname, const char *filename) default: log_variadic_message(LMT_WARNING, - _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); + _("Unknown category '0x%02x' in plugin '%s'..."), + category, self->ob_type->tp_name); break; } } - /* Conclusion */ - - return G_PLUGIN_MODULE(result); - - gppn_bad_plugin: - - gppn_interface_error: - - g_object_unref(G_OBJECT(result)); - - return NULL; - - gppn_no_instance: - - gppn_no_class: - - Py_DECREF(module); - - gppn_bad_exit: - - return NULL; + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* unused = variable non utilisé pour l'usage de __VA_ARGS__. * * * -* Description : Reconstruit la déclaration d'interface à partir de lectures. * +* Description : Accompagne la fin du chargement des modules natifs. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_read_interface(GPythonPlugin *plugin) +static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *plugin, PluginAction action) { - bool result; /* Bilan à renvoyer */ - plugin_interface interface; /* Recueil des éléments */ - PyObject *desc; /* Tableau de description */ - PyObject *str; /* Chaîne de caractères */ - PyObject *tuple; /* Liste d'éléments à traiter */ - Py_ssize_t count; /* Nombre d'éléments présents */ - Py_ssize_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ - plugin_interface *final; /* Interface finale conservée */ - - result = true; - - desc = run_python_method(plugin->instance, "get_interface", NULL); - if (!PyDict_Check(desc)) - { - result = false; - goto pgpri_end; - } - - memset(&interface, 0, sizeof(interface)); - - /* Chargements des premières chaînes */ - -#define READ_STR_FIELD(name) \ - str = PyDict_GetItemString(desc, #name); \ - if ((result = PyUnicode_Check(str))) \ - interface.name = strdup(PyUnicode_DATA(str)); \ - - READ_STR_FIELD(name); - READ_STR_FIELD(desc); - READ_STR_FIELD(version); - - /* Chargement des actions supportées */ - - tuple = PyDict_GetItemString(desc, "actions"); - - if (!PyList_Check(tuple)) - { - result = false; - goto pgpri_failed; - } - - count = PyList_GET_SIZE(tuple); - - interface.actions = (plugin_action_t *)calloc(count, sizeof(plugin_action_t)); - interface.actions_count = count; - - for (i = 0; i < count; i++) - { - action = PyList_GET_ITEM(tuple, i); - - interface.actions[i] = PyLong_AsUnsignedLong(action); - - } - - pgpri_failed: - - if (result) - { - final = (plugin_interface *)calloc(1, sizeof(plugin_interface)); - - memcpy(final, &interface, sizeof(interface)); - - final->required = (const char **)malloc(sizeof(char *)); - final->required[0] = "PyChrysalide"; - final->required_count = 1; - - G_PLUGIN_MODULE(plugin)->interface = final; - - } - else - { - if (interface.name != NULL) free((char *)interface.name); - if (interface.desc != NULL) free((char *)interface.desc); - if (interface.version != NULL) free((char *)interface.version); - - if (interface.actions != NULL) free(interface.actions); - - } - - pgpri_end: - - Py_XDECREF(desc); - - return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * -* ref = espace de référencement global. * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* resources = liste de ressources à constituer. [OUT] * +* count = taille de cette liste. [OUT] * * * -* Description : Procède à l'initialisation du greffon. * +* Description : Complète une liste de resources pour thème. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_do_init(GPythonPlugin *plugin) +static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin, PluginAction action, char ***resources, size_t *count) { - bool result; /* Bilan à retourner */ - PyThreadState *tstate; /* Contexte d'environnement */ - PyObject *value; /* Valeur obtenue */ - - tstate = PyThreadState_Get(); - - if (tstate != NULL) - PyEval_RestoreThread(tstate); - - if (!has_python_method(plugin->instance, "init")) - result = true; - - else - { - value = run_python_method(plugin->instance, "init", NULL); - - result = (value != NULL && PyObject_IsTrue(value)); - - Py_XDECREF(value); - - } - - if (tstate != NULL) - PyEval_SaveThread(); - - return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à initialiser. * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* content = contenu binaire à traiter. * +* wid = identifiant du groupe de traitement. * +* status = barre de statut à tenir informée. * * * -* Description : Procède à l'extinction du greffon. * +* Description : Procède à une opération liée à un contenu binaire. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool g_python_plugin_do_exit(GPythonPlugin *plugin) +static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) { - bool result; /* Bilan à retourner */ - PyObject *value; /* Valeur obtenue */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *value; /* Valeurs obtenues */ - if (!has_python_method(plugin->instance, "exit")) - result = true; + gstate = PyGILState_Ensure(); - else - { - value = run_python_method(plugin->instance, "exit", NULL); + pyobj = pygobject_new(G_OBJECT(plugin)); - result = (value != NULL && PyObject_IsTrue(value)); + assert(has_python_method(pyobj, "handle_binary_content")); - Py_XDECREF(value); + args = PyTuple_New(4); - } + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(content))); + PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(wid)); + PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - return result; + value = run_python_method(pyobj, "handle_binary_content", args); + + Py_XDECREF(value); + Py_DECREF(args); + + PyGILState_Release(gstate); } @@ -689,11 +559,11 @@ static bool g_python_plugin_do_exit(GPythonPlugin *plugin) * * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * -* content = contenu binaire à traiter. * +* content = contenu chargé à traiter. * * wid = identifiant du groupe de traitement. * * status = barre de statut à tenir informée. * * * -* Description : Procède à une opération liée à un contenu binaire. * +* Description : Procède à une opération liée à un contenu chargé. * * * * Retour : - * * * @@ -701,14 +571,19 @@ static bool g_python_plugin_do_exit(GPythonPlugin *plugin) * * ******************************************************************************/ -static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) +static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedContent *content, wgroup_id_t wid, GtkStatusStack *status) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "handle_loaded_content")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -716,7 +591,7 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(wid)); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "handle_content", args); + value = run_python_method(pyobj, "handle_loaded_content", args); Py_XDECREF(value); Py_DECREF(args); @@ -742,14 +617,19 @@ static void g_python_plugin_handle_binary_content(const GPythonPlugin *plugin, P * * ******************************************************************************/ -static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status) +static bool py_plugin_module_handle_binary_format_analysis_wrapper(const GPluginModule *plugin, PluginAction action, GBinFormat *format, wgroup_id_t gid, GtkStatusStack *status) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "handle_format_analysis")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -757,7 +637,7 @@ static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *p PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(gid)); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "handle_format_analysis", args); + value = run_python_method(pyobj, "handle_format_analysis", args); Py_XDECREF(value); Py_DECREF(args); @@ -785,14 +665,19 @@ static bool g_python_plugin_handle_binary_format_analysis(const GPythonPlugin *p * * ******************************************************************************/ -static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) +static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *plugin, PluginAction action, GBinFormat *format, GPreloadInfo *info, GtkStatusStack *status) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "preload_format")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -800,7 +685,7 @@ static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, P PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(info))); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(status))); - value = run_python_method(plugin->instance, "preload_format", args); + value = run_python_method(pyobj, "preload_format", args); Py_XDECREF(value); Py_DECREF(args); @@ -826,20 +711,25 @@ static bool g_python_plugin_preload_binary_format(const GPythonPlugin *plugin, P * * ******************************************************************************/ -static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, PluginAction action, GExeFormat *format) +static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *plugin, PluginAction action, GExeFormat *format) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "attach_debug_format")); + args = PyTuple_New(2); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format))); - value = run_python_method(plugin->instance, "attach_debug_format", args); + value = run_python_method(pyobj, "attach_debug_format", args); Py_XDECREF(value); Py_DECREF(args); @@ -851,9 +741,9 @@ static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, Plu /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* binary = binaire dont le contenu est en cours de traitement.* +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* binary = binaire dont le contenu est en cours de traitement. * * status = barre de statut à tenir informée. * * context = contexte de désassemblage. * * * @@ -865,14 +755,19 @@ static void g_python_plugin_attach_debug_format(const GPythonPlugin *plugin, Plu * * ******************************************************************************/ -static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) +static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *plugin, PluginAction action, GLoadedBinary *binary, GtkStatusStack *status, GProcContext *context) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *value; /* Valeurs obtenues */ gstate = PyGILState_Ensure(); + pyobj = pygobject_new(G_OBJECT(plugin)); + + assert(has_python_method(pyobj, "process_disassembly")); + args = PyTuple_New(4); PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); @@ -880,7 +775,7 @@ static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAc PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(status))); PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(context))); - value = run_python_method(plugin->instance, "process_disassembly", args); + value = run_python_method(pyobj, "process_disassembly", args); Py_XDECREF(value); Py_DECREF(args); @@ -890,6 +785,265 @@ static void g_python_plugin_process_disass(const GPythonPlugin *plugin, PluginAc } +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* action = type d'action attendue. * +* content = élément chargé à consulter. * +* version = précise si les versions doivent être recherchées. * +* names = désignations humaines correspondantes, à libérer. * +* count = nombre de types d'obscurcissement trouvés. [OUT] * +* * +* Description : Effectue la détection d'effets d'outils externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *plugin, PluginAction action, const GLoadedContent *content, bool version, char ***names, size_t *count) +{ + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERFACE INTERNE POUR GREFFONS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini par la GLib pour le greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise l'instance d'un greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_dispose(GPythonPlugin *plugin) +{ + PyThreadState *tstate; /* Contexte d'environnement */ + + /** + * Cf. https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock + * + * Cependant, comme on se trouve à priori dans le thread principal de l'interpréteur, + * PyGILState_Ensure() ne pose aucun verrou. Ce qui aboutit à la situation suivante : + * + * Fatal Python error: drop_gil: GIL is not locked + * + * On peut forcer les choses avec PyEval_AcquireLock(), mais cette fonction est marquée + * comme dépréciée depuis Python 3.2. + * + * Donc on choisit les alternatives officielles. + * + * Cependant, PyThreadState_Get() renvoit l'erreur suivante : + * + * Fatal Python error: PyThreadState_Get: no current thread + * + * Donc on se rabat sur une sauvegarde, qui n'est initialisée que lorsque l'interpréteur + * est intégré dans l'éditeur. + */ + + tstate = get_pychrysalide_main_tstate(); + + if (tstate != NULL) + PyEval_RestoreThread(tstate); + + Py_XDECREF(plugin->instance); + plugin->instance = NULL; + + if (tstate != NULL) + PyEval_SaveThread(); + + G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_finalize(GPythonPlugin *plugin) +{ + plugin_interface *final; /* Interface finale conservée */ + + Py_XDECREF(plugin->module); + + final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + + if (final != NULL) + { + assert(final->required_count == 1); + free(final->required); + + free(final); + + } + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : modname = nom du module à charger. * +* filename = chemin d'accès au code Python à charger. * +* * +* Description : Crée un greffon à partir de code Python. * +* * +* Retour : Adresse de la structure mise en place ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPluginModule *g_python_plugin_new(const char *modname, const char *filename) +{ + GPythonPlugin *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *err_type; /* Type d'erreur Python */ + PyObject *err_value; /* Instance Python d'erreur */ + PyObject *err_traceback; /* Trace Python associée */ + PyObject *err_string; /* Description Python d'erreur */ + const char *err_msg; /* Représentation humaine */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ + + name = PyUnicode_FromString(modname); + if (name == NULL) goto gppn_bad_exit; + + module = PyImport_Import(name); + Py_DECREF(name); + + if (PyErr_Occurred()) + { + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if (err_value == NULL) + log_variadic_message(LMT_ERROR, + _("An unknown error occured when importing '%s'..."), modname); + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + log_variadic_message(LMT_ERROR, + _("An error occured when importing '%s': \"%s\""), modname, err_msg); + + Py_DECREF(err_string); + Py_DECREF(err_value); + + } + + Py_XDECREF(err_traceback); + Py_XDECREF(err_type); + + Py_XDECREF(module); + + module = NULL; + + } + + if (module == NULL) goto gppn_bad_exit; + + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); + + if (class == NULL) goto gppn_no_class; + if (!PyType_Check(class->ob_type)) goto gppn_no_class; + + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto gppn_no_instance; + + result = G_PYTHON_PLUGIN(pygobject_get(instance)); + + G_PLUGIN_MODULE(result)->filename = strdup(filename); + + result->module = module; + result->instance = instance; + + Py_INCREF(instance); + + return G_PLUGIN_MODULE(result); + + gppn_no_instance: + + gppn_no_class: + + Py_DECREF(module); + + gppn_bad_exit: + + return NULL; + +} + + /* ---------------------------------------------------------------------------------- */ /* MODULE PYTHON POUR LES SCRIPTS */ @@ -926,7 +1080,7 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) case LMT_BAD_BINARY: case LMT_ERROR: case LMT_EXT_ERROR: - /*g_plugin_module_*/log_simple_message(/*G_PLUGIN_MODULE(pygobject_get(self)), */type, msg); + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); result = Py_None; Py_INCREF(result); break; @@ -1037,7 +1191,10 @@ PyTypeObject *get_python_plugin_module_type(void) .tp_doc = "Chrysalide plugin for Python.", .tp_methods = py_plugin_module_methods, - .tp_getset = py_plugin_module_getseters + .tp_getset = py_plugin_module_getseters, + + .tp_init = py_plugin_module_init, + .tp_new = py_plugin_module_new, }; @@ -1072,7 +1229,7 @@ bool ensure_python_plugin_module_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type, &PyGObject_Type)) + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type, &PyGObject_Type)) return false; if (!py_plugin_module_define_constants(type)) diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c index 8849981..502bf20 100644 --- a/plugins/pychrysalide/pychrysa.c +++ b/plugins/pychrysalide/pychrysa.c @@ -45,7 +45,6 @@ #include "access.h" #include "constval.h" -#include "dt.h" #include "helpers.h" #include "plugin.h" #include "star.h" @@ -63,7 +62,7 @@ -DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Provides bindings to Python", "0.1.0", +DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("GPyChrysalidePlugin", "PyChrysalide", "Provides bindings to Python", "0.1.0", EMPTY_PG_LIST(.required), AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT, PGA_NATIVE_LOADED)); @@ -368,8 +367,6 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) /* Mise en place des fonctionnalités offertes */ - init_dynamic_python_types(); - result = PyModule_Create(&py_chrysalide_module); register_access_to_python_module(py_chrysalide_module.m_name, result); |