diff options
Diffstat (limited to 'plugins/pychrysalide')
94 files changed, 10042 insertions, 4924 deletions
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index 183a4ef..c574727 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -1,7 +1,7 @@ DEFAULT_INCLUDES = -I$(top_builddir) -idirafter. -lib_LTLIBRARIES = pychrysalide.la +lib_LTLIBRARIES = pychrysalide.la pychrysalideui.la libdir = $(pluginslibdir) @@ -15,12 +15,6 @@ endif # if BUILD_GTK_SUPPORT -# GTKEXT_LIBADD = \ -# gtkext/libpychrysagtkext.la - -# GTKEXT_SUBDIR = \ -# gtkext - # GUI_LIBADD = \ # gui/libpychrysagui.la @@ -32,7 +26,10 @@ endif pychrysalide_la_SOURCES = \ access.h access.c \ + bindings.h bindings.c \ constants.h constants.c \ + convert.h convert.c \ + core-int.h \ core.h core.c \ helpers.h helpers.c \ star.h star.c \ @@ -48,7 +45,6 @@ AM_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(TOOLKIT_CFL # common/libpychrysacommon.la \ # core/libpychrysacore.la \ # debug/libpychrysadebug.la \ -# $(GTKEXT_LIBADD) \ # $(GUI_LIBADD) \ # mangling/libpychrysamangling.la \ # plugins/libpychrysaplugins.la @@ -71,10 +67,32 @@ pychrysalide_la_LDFLAGS = \ $(RUN_PATH) +EXTRA_pychrysalideui_la_DEPENDENCIES = pychrysalide.la + +pychrysalideui_la_SOURCES = \ + core-ui-int.h \ + core-ui.h core-ui.c \ + helpers-ui.h helpers-ui.c + +pychrysalideui_la_LIBADD = \ + arch/libpychrysaarchui.la \ + glibext/libpychrysaglibextui.la \ + gtkext/libpychrysagtkext.la + +# -ldl: dladdr(), dlerror() +pychrysalideui_la_LDFLAGS = \ + -module -avoid-version -ldl \ + $(LIBPYTHON_INTERPRETER_LIBS) \ + $(LIBPYGOBJECT_LIBS) \ + -L.libs -l:pychrysalide.so \ + -L$(top_srcdir)/src/.libs -lchrysacoreui\ + $(RUN_PATH) + + devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(pychrysalide_la_SOURCES:%c=) #SUBDIRS = analysis arch common core debug $(GTKEXT_SUBDIR) $(GUI_SUBDIR) mangling plugins -SUBDIRS = analysis arch common core format glibext plugins +SUBDIRS = analysis arch common core format glibext gtkext plugins diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c index dd9c1c1..c271139 100644 --- a/plugins/pychrysalide/analysis/content.c +++ b/plugins/pychrysalide/analysis/content.c @@ -50,9 +50,9 @@ /* Initialise la classe générique des contenus de binaire. */ -static void py_binary_content_init_gclass(GBinContentClass *, gpointer); +static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_binary_content_init(PyObject *, PyObject *, PyObject *); @@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des contenus de binaire. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused) +static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass) { - class->describe = py_binary_content_describe_wrapper; + PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper); + + PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper); - class->compute_checksum = py_binary_content_compute_checksum_wrapper; + PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper); - class->compute_size = py_binary_content_compute_size_wrapper; - class->compute_start_pos = py_binary_content_compute_start_pos_wrapper; - class->compute_end_pos = py_binary_content_compute_end_pos_wrapper; + PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper); - class->seek = py_binary_content_seek_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper); - class->read_raw = py_binary_content_read_raw_wrapper; - class->read_u8 = py_binary_content_read_u8_wrapper; - class->read_u16 = py_binary_content_read_u16_wrapper; - class->read_u32 = py_binary_content_read_u32_wrapper; - class->read_u64 = py_binary_content_read_u64_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper); - class->read_uleb128 = py_binary_content_read_uleb128_wrapper; - class->read_leb128 = py_binary_content_read_leb128_wrapper; + return 0; } @@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type)) return false; diff --git a/plugins/pychrysalide/arch/Makefile.am b/plugins/pychrysalide/arch/Makefile.am index d3ee3f0..a0dcfdb 100644 --- a/plugins/pychrysalide/arch/Makefile.am +++ b/plugins/pychrysalide/arch/Makefile.am @@ -1,19 +1,13 @@ -noinst_LTLIBRARIES = libpychrysaarch4.la # libpychrysaarch.la +noinst_LTLIBRARIES = libpychrysaarch4.la libpychrysaarchui.la # libpychrysaarch.la # libpychrysaarch_la_SOURCES = \ -# constants.h constants.c \ # context.h context.c \ # instriter.h instriter.c \ -# instruction.h instruction.c \ -# module.h module.c \ -# operand.h operand.c \ -# processor.h processor.c \ -# register.h register.c \ -# vmpa.h vmpa.c +# processor.h processor.c # libpychrysaarch_la_LIBADD = \ -# instructions/libpychrysaarchinstructions.la \ +# \ # operands/libpychrysaarchoperands.la # libpychrysaarch_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ @@ -22,21 +16,33 @@ noinst_LTLIBRARIES = libpychrysaarch4.la # libpychrysaarch.la libpychrysaarch4_la_SOURCES = \ constants.h constants.c \ + instruction.h instruction.c \ module.h module.c \ + operand.h operand.c \ + register.h register.c \ vmpa.h vmpa.c -# libpychrysaarch4_la_LIBADD = \ -# instructions/libpychrysaarchinstructions.la \ -# operands/libpychrysaarchoperands.la +libpychrysaarch4_la_LIBADD = \ + instructions/libpychrysaarchinstructions.la \ + operands/libpychrysaarchoperands.la libpychrysaarch4_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ $(TOOLKIT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT +libpychrysaarchui_la_SOURCES = \ + module-ui.h module-ui.c \ + operand-ui.h operand-ui.c + +libpychrysaarchui_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + $(TOOLKIT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(libpychrysaarch_la_SOURCES:%c=) -# SUBDIRS = instructions operands +SUBDIRS = instructions operands diff --git a/plugins/pychrysalide/arch/constants.c b/plugins/pychrysalide/arch/constants.c index 3604795..5db59ff 100644 --- a/plugins/pychrysalide/arch/constants.c +++ b/plugins/pychrysalide/arch/constants.c @@ -25,7 +25,7 @@ #include "constants.h" -//#include <arch/instruction.h> +#include <arch/instruction.h> //#include <arch/processor.h> #include <arch/vmpa.h> @@ -33,7 +33,6 @@ #include "../helpers.h" -#if 0 // FIXME /****************************************************************************** * * @@ -116,6 +115,8 @@ bool define_arch_instruction_constants(PyTypeObject *type) } +#if 0 // FIXME + /****************************************************************************** * * * Paramètres : type = type dont le dictionnaire est à compléter. * diff --git a/plugins/pychrysalide/arch/constants.h b/plugins/pychrysalide/arch/constants.h index b12579e..2f16c4f 100644 --- a/plugins/pychrysalide/arch/constants.h +++ b/plugins/pychrysalide/arch/constants.h @@ -30,10 +30,13 @@ #include <stdbool.h> -#if 0 // FIXME + /* Définit les constantes relatives aux instructions. */ bool define_arch_instruction_constants(PyTypeObject *); + +#if 0 // FIXME + /* Définit les constantes relatives aux processeurs. */ bool define_arch_processor_constants(PyTypeObject *); diff --git a/plugins/pychrysalide/arch/instruction.c b/plugins/pychrysalide/arch/instruction.c index 0a9ba16..49daa9c 100644 --- a/plugins/pychrysalide/arch/instruction.c +++ b/plugins/pychrysalide/arch/instruction.c @@ -27,13 +27,12 @@ #include <assert.h> #include <malloc.h> -#include <string.h> #include <pygobject.h> #include <i18n.h> +#include <plugins/self.h> #include <arch/instruction-int.h> -#include <plugins/dt.h> #include "constants.h" @@ -41,30 +40,38 @@ #include "vmpa.h" #include "../access.h" #include "../helpers.h" -#include "../glibext/linegen.h" - - - -static G_DEFINE_QUARK(cached_keyword, get_cached_keyword); +#include "../glibext/objhole.h" +#include "../glibext/serialize.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_instruction_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe générique des instructions. */ -static void py_arch_instruction_init_gclass(GArchInstructionClass *, gpointer); +static int py_arch_instruction_init_gclass(GArchInstructionClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_instruction, G_TYPE_ARCH_INSTRUCTION, py_arch_instruction_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_instruction, G_TYPE_ARCH_INSTRUCTION); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_arch_instruction_init(PyObject *, PyObject *, PyObject *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static char *py_arch_instruction_get_encoding_wrapper(const GArchInstruction *); + /* Fournit le nom humain de l'instruction manipulée. */ -static const char *py_arch_instruction_get_class_keyword(GArchInstruction *); +static char *py_arch_instruction_get_keyword_wrapper(const GArchInstruction *); + + + +/* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ + + +/* Fournit les origines d'une instruction donnée. */ +static PyObject *py_arch_instruction_get_sources(PyObject *, void *); + +/* Fournit les destinations d'une instruction donnée. */ +static PyObject *py_arch_instruction_get_destinations(PyObject *, void *); @@ -72,10 +79,7 @@ static const char *py_arch_instruction_get_class_keyword(GArchInstruction *); /* Attache un opérande supplémentaire à une instruction. */ -static PyObject *py_arch_instruction_attach_extra_operand(PyObject *, PyObject *); - -/* Fournit tous les opérandes d'une instruction. */ -static PyObject *py_arch_instruction_get_operands(PyObject *, void *); +static PyObject *py_arch_instruction_attach_operand(PyObject *, PyObject *); /* Remplace un opérande d'une instruction par un autre. */ static PyObject *py_arch_instruction_replace_operand(PyObject *, PyObject *); @@ -89,34 +93,35 @@ static PyObject *py_arch_instruction_find_operand_path(PyObject *, PyObject *); /* Obtient l'opérande correspondant à un chemin donné. */ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *, PyObject *); +/* Fournit tous les opérandes d'une instruction. */ +static PyObject *py_arch_instruction_get_operands(PyObject *, void *); -/* ------------------- DEFINITION DES LIAISONS ENTRE INSTRUCTIONS ------------------- */ +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ -/* Fournit les origines d'une instruction donnée. */ -static PyObject *py_arch_instruction_get_sources(PyObject *, void *); -/* Fournit les destinations d'une instruction donnée. */ -static PyObject *py_arch_instruction_get_destinations(PyObject *, void *); +/* Ajoute une information complémentaire à une instruction. */ +static PyObject *py_arch_instruction_set_flag(PyObject *, PyObject *); +/* Retire une information complémentaire à une instruction. */ +static PyObject *py_arch_instruction_unset_flag(PyObject *, PyObject *); +/* Détermine si une instruction possède un fanion particulier. */ +static PyObject *py_arch_instruction_has_flag(PyObject *, PyObject *); -/* --------------------- INSTRUCTIONS D'ARCHITECTURES EN PYTHON --------------------- */ +/* Fournit l'identifiant correspondant à un type d'instructions. */ +static PyObject *py_arch_instruction_get_type_id(PyObject *, void *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static PyObject *py_arch_instruction_get_encoding(PyObject *, void *); -/* Fournit l'identifiant unique pour un ensemble d'instructions. */ -static PyObject *py_arch_instruction_get_unique_id(PyObject *, void *); +/* Indique l'encodage d'une instruction de façon détaillée. */ +static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); /* Fournit la place mémoire d'une instruction. */ static PyObject *py_arch_instruction_get_range(PyObject *, void *); -/* Définit la localisation d'une instruction. */ -static int py_arch_instruction_set_range(PyObject *, PyObject *, void *); - -/* Fournit le nom humain de l'instruction manipulée. */ -static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); - /* ---------------------------------------------------------------------------------- */ @@ -126,24 +131,23 @@ static PyObject *py_arch_instruction_get_keyword(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des instructions. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_instruction_init_gclass(GArchInstructionClass *class, gpointer unused) +static int py_arch_instruction_init_gclass(GArchInstructionClass *gclass, PyTypeObject *pyclass) { - GArchInstructionClass *instr; /* Encore une autre vision... */ - - instr = G_ARCH_INSTRUCTION_CLASS(class); + PY_CLASS_SET_WRAPPER(gclass->get_encoding, py_arch_instruction_get_encoding_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_keyword, py_arch_instruction_get_keyword_wrapper); - instr->get_keyword = (get_instruction_keyword_fc)py_arch_instruction_get_class_keyword; + return 0; } @@ -164,17 +168,25 @@ static void py_arch_instruction_init_gclass(GArchInstructionClass *class, gpoint static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { - unsigned short int uid; /* Indentifiant unique de type */ - const char *keyword; /* Désignation d'instruction */ + unsigned short int tid; /* Indentifiant unique de type */ int ret; /* Bilan de lecture des args. */ GArchInstruction *instr; /* Instruction à manipuler */ - GQuark cache_key; /* Emplacement local */ - static char *kwlist[] = { "uid", "keyword", NULL }; +#define ARCH_INSTRUCTION_DOC \ + "The ArchInstruction object provides a base class for instructions" \ + " of any architecture.\n" \ + " operands of any kind for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " one argument: an unique identifier, as an integer value.\n" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._get_encoding();\n" \ + "* pychrysalide.arch.ArchRegister._get_keyword().\n" /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "Hs", kwlist, &uid, &keyword); + ret = PyArg_ParseTuple(args, "H", &tid); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -186,13 +198,72 @@ static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kw instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - cache_key = get_cached_keyword_quark(); + if (!g_arch_instruction_create(instr, tid)) + return -1; + + return 0; - g_object_set_qdata_full(G_OBJECT(instr), cache_key, strdup(keyword), g_free); +} - g_arch_instruction_set_unique_id(G_ARCH_INSTRUCTION(instr), uid); - return 0; +/****************************************************************************** +* * +* Paramètres : instr = instruction quelconque à consulter. * +* * +* Description : Indique l'encodage d'une instruction de façon détaillée. * +* * +* Retour : Description humaine de l'encodage utilisé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_arch_instruction_get_encoding_wrapper(const GArchInstruction *instr) +{ + char *result; /* Encodage à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_GET_ENCODING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_encoding, "$self, /", \ + METH_NOARGS, \ + "Abstract method describing the encoding related to an" \ + " instruction.\n" \ + "\n" \ + "The result should be the string value.\n" \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(instr)); + + if (has_python_method(pyobj, "_get_encoding")) + { + pyret = run_python_method(pyobj, "_get_encoding", NULL); + + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + log_variadic_message(LMT_ERROR, _("The returned raw name must be a string")); + + else + result = strdup(PyUnicode_DATA(pyret)); + + } + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; } @@ -209,15 +280,50 @@ static int py_arch_instruction_init(PyObject *self, PyObject *args, PyObject *kw * * ******************************************************************************/ -static const char *py_arch_instruction_get_class_keyword(GArchInstruction *instr) +static char *py_arch_instruction_get_keyword_wrapper(const GArchInstruction *instr) { - const char *result; /* Désignation à retourner */ - GQuark cache_key; /* Emplacement local */ + char *result; /* Etiquette à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ + +#define PLUGIN_MODULE_GET_KEYWORD_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_keyword, "$self, /", \ + METH_NOARGS, \ + "Abstract method giving the official name of the assembly" \ + " instruction.\n" \ + "\n" \ + "The result should be the string value.\n" \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(instr)); + + if (has_python_method(pyobj, "_get_keyword")) + { + pyret = run_python_method(pyobj, "_get_keyword", NULL); + + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + log_variadic_message(LMT_ERROR, _("The returned raw name must be a string")); - cache_key = get_cached_keyword_quark(); + else + result = strdup(PyUnicode_DATA(pyret)); - result = g_object_get_qdata(G_OBJECT(instr), cache_key); - assert(result != NULL); + } + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); return result; @@ -226,94 +332,201 @@ static const char *py_arch_instruction_get_class_keyword(GArchInstruction *instr /* ---------------------------------------------------------------------------------- */ -/* MANIPULATION DES OPERANDES */ +/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : self = architecture concernée par la procédure. * -* args = instruction représentant le point de départ. * +* Paramètres : self = instruction d'architecture à manipuler. * +* unused = adresse non utilisée ici. * * * -* Description : Attache un opérande supplémentaire à une instruction. * +* Description : Fournit les origines d'une instruction donnée. * * * -* Retour : None. * +* Retour : Nombre de ces origines. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_attach_extra_operand(PyObject *self, PyObject *args) +static PyObject *py_arch_instruction_get_sources(PyObject *self, void *unused) { - GArchOperand *op; /* Opérande concerné à ajouter */ - int ret; /* Bilan de lecture des args. */ - GArchInstruction *instr; /* Instruction manipulée */ + PyObject *result; /* Instance à retourner */ + GArchInstruction *instr; /* Version native */ + size_t count; /* Nombre de liens présents */ + size_t i; /* Boucle de parcours */ - ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &op); - if (!ret) return NULL; + + GArchInstruction *src; /* Instruction en source */ + InstructionLinkType src_type; /* Type de lien */ + PyObject *linked; /* Source de lien Python */ + PyObject *lnk_type; /* Nature du lien en Python */ +#ifndef NDEBUG + int ret; /* Bilan d'une écriture d'arg. */ +#endif + +#define ARCH_INSTRUCTION_SOURCES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + sources, py_arch_instruction, \ + "Provide the instructions list driving to the current instruction.\n" \ + "\n" \ + "Each item of the resulting tuple is a pair of" \ + " pychrysalide.arch.ArchInstruction instance and" \ + " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_object_ref(G_OBJECT(op)); + g_thick_object_lock(G_THICK_OBJECT(instr)); + + count = g_arch_instruction_count_src_links(instr); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + src = g_arch_instruction_get_linked_source(instr, i, &src_type); + + linked = pygobject_new(G_OBJECT(src)); + lnk_type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), + "InstructionLinkType", src_type); + +#ifndef NDEBUG + ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); + assert(ret == 0); +#else + PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); +#endif + + unref_object(src); + + } - g_arch_instruction_attach_extra_operand(instr, op); + g_thick_object_unlock(G_THICK_OBJECT(instr)); - Py_RETURN_NONE; + return result; } /****************************************************************************** * * -* Paramètres : self = objet représentant une instruction. * +* Paramètres : self = instruction d'architecture à manipuler. * * unused = adresse non utilisée ici. * * * -* Description : Fournit tous les opérandes d'une instruction. * +* Description : Fournit les destinations d'une instruction donnée. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Nombre de ces destinations. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_operands(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_destinations(PyObject *self, void *unused) { PyObject *result; /* Instance à retourner */ GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre d'opérandes présents */ + size_t count; /* Nombre de liens présents */ size_t i; /* Boucle de parcours */ - GArchOperand *operand; /* Opérande à manipuler */ - PyObject *opobj; /* Version Python */ + GArchInstruction *dest; /* Instruction en source */ + InstructionLinkType dest_type; /* Type de lien */ + PyObject *linked; /* Destination de lien Python */ + PyObject *lnk_type; /* Nature du lien en Python */ #ifndef NDEBUG int ret; /* Bilan d'une écriture d'arg. */ #endif +#define ARCH_INSTRUCTION_DESTINATIONS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + destinations, py_arch_instruction, \ + "Provide the instructions list following the current instruction.\n" \ + "\n" \ + "Each item of the resulting tuple is a pair of" \ + " pychrysalide.arch.ArchInstruction instance and" \ + " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +) + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_operands(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); - count = _g_arch_instruction_count_operands(instr); + count = g_arch_instruction_count_dest_links(instr); result = PyTuple_New(count); for (i = 0; i < count; i++) { - operand = _g_arch_instruction_get_operand(instr, i); + dest = g_arch_instruction_get_linked_destination(instr, i, &dest_type); - opobj = pygobject_new(G_OBJECT(operand)); + linked = pygobject_new(G_OBJECT(dest)); + lnk_type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), + "InstructionLinkType", dest_type); #ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); + ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); assert(ret == 0); #else - PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); + PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, lnk_type)); #endif - g_object_unref(G_OBJECT(operand)); + unref_object(dest); } - g_arch_instruction_unlock_operands(instr); + g_thick_object_unlock(G_THICK_OBJECT(instr)); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES OPERANDES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Attache un opérande supplémentaire à une instruction. * +* * +* Retour : None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_instruction_attach_operand(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GArchOperand *op; /* Opérande concerné à ajouter */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + +#define ARCH_INSTRUCTION_ATTACH_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + attach_operand, "$self, operand, /", \ + METH_VARARGS, py_arch_instruction, \ + "Add an extra operand to an instruction.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "The *operand* argument has to be a pychrysalide.arch.ArchOperand" \ + " instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &op); + if (!ret) return NULL; + + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); + + g_arch_instruction_attach_operand(instr, op); + + result = Py_None; + Py_INCREF(result); return result; @@ -342,6 +555,21 @@ static PyObject *py_arch_instruction_replace_operand(PyObject *self, PyObject *a GArchInstruction *instr; /* Instruction manipulée */ bool status; /* Bilan de l'opération */ +#define ARCH_INSTRUCTION_REPLACE_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + replace_operand, "$self, old, new, /", \ + METH_VARARGS, py_arch_instruction, \ + "Replace an old instruction operand by a another one.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "Both the *old* and *new* arguments have to be a" \ + " pychrysalide.arch.ArchOperand instance.\n" \ + "\n" \ + "The status of the operation is returned as a boolean value: *True*"\ + " if the operand has been replaced, *False* in case of failure." \ +) + ret = PyArg_ParseTuple(args, "O&O&", convert_to_arch_operand, &old, convert_to_arch_operand, &new); if (!ret) return NULL; @@ -349,9 +577,6 @@ static PyObject *py_arch_instruction_replace_operand(PyObject *self, PyObject *a status = g_arch_instruction_replace_operand(instr, old, new); - if (status) - g_object_ref(G_OBJECT(new)); - result = status ? Py_True : Py_False; Py_INCREF(result); @@ -381,6 +606,21 @@ static PyObject *py_arch_instruction_detach_operand(PyObject *self, PyObject *ar GArchInstruction *instr; /* Instruction manipulée */ bool status; /* Bilan de l'opération */ +#define ARCH_INSTRUCTION_DETACH_OPERAND_METHOD PYTHON_METHOD_DEF \ +( \ + detach_operand, "$self, operand, /", \ + METH_VARARGS, py_arch_instruction, \ + "Remove an operand from the instruction.\n" \ + "\n" \ + "The instruction has to be locked during the instruction.\n" \ + "\n" \ + "The *operand* argument has to be a pychrysalide.arch.ArchOperand" \ + " instance.\n" \ + "\n" \ + "The status of the operation is returned as a boolean value: *True*"\ + " if the operand has been removed, *False* in case of failure." \ +) + ret = PyArg_ParseTuple(args, "O&", convert_to_arch_operand, &target); if (!ret) return NULL; @@ -500,7 +740,7 @@ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *self, PyObj if (op != NULL) { result = pygobject_new(G_OBJECT(op)); - g_object_unref(G_OBJECT(op)); + unref_object(op); } else { @@ -513,182 +753,258 @@ static PyObject *py_arch_instruction_get_operand_from_path(PyObject *self, PyObj } - -/* ---------------------------------------------------------------------------------- */ -/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */ -/* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : self = instruction d'architecture à manipuler. * +* Paramètres : self = objet représentant une instruction. * * unused = adresse non utilisée ici. * * * -* Description : Fournit les origines d'une instruction donnée. * +* Description : Fournit tous les opérandes d'une instruction. * * * -* Retour : Nombre de ces origines. * +* Retour : Valeur associée à la propriété consultée. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_sources(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_operands(PyObject *self, void *unused) { PyObject *result; /* Instance à retourner */ GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre de liens présents */ + size_t count; /* Nombre d'opérandes présents */ size_t i; /* Boucle de parcours */ - const instr_link_t *source; /* Origine des liens */ - PyObject *linked; /* Source de lien Python */ - PyObject *type; /* Nature du lien en Python */ + GArchOperand *operand; /* Opérande à manipuler */ + PyObject *opobj; /* Version Python */ #ifndef NDEBUG int ret; /* Bilan d'une écriture d'arg. */ #endif -#define ARCH_INSTRUCTION_SOURCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - sources, py_arch_instruction, \ - "Provide the instructions list driving to the current instruction.\n" \ - "\n" \ - "Each item of the resulting tuple is a pair of" \ - " pychrysalide.arch.ArchInstruction instance and" \ - " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ +#define ARCH_INSTRUCTION_OPERANDS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + operands, py_arch_instruction, \ + "List of instruction attached operands.\n" \ + "\n" \ + "The result is a list of pychrysalide.arch.ArchOperand" \ + " instances, which can be empty." \ ) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_src(instr); + g_thick_object_lock(G_THICK_OBJECT(instr)); - count = g_arch_instruction_count_sources(instr); + count = g_arch_instruction_count_operands(instr); result = PyTuple_New(count); for (i = 0; i < count; i++) { - source = g_arch_instruction_get_source(instr, i); + operand = g_arch_instruction_get_operand(instr, i); - linked = pygobject_new(G_OBJECT(source->linked)); - type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), - "InstructionLinkType", source->type); + opobj = pygobject_new(G_OBJECT(operand)); #ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); + ret = PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); assert(ret == 0); #else - PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); + PyTuple_SetItem(result, i, Py_BuildValue("O", opobj)); #endif - unref_instr_link(source); + unref_object(operand); } - g_arch_instruction_unlock_src(instr); + g_thick_object_unlock(G_THICK_OBJECT(instr)); return result; } + +/* ---------------------------------------------------------------------------------- */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : self = instruction d'architecture à manipuler. * -* unused = adresse non utilisée ici. * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * * * -* Description : Fournit les destinations d'une instruction donnée. * +* Description : Ajoute une information complémentaire à une instruction. * * * -* Retour : Nombre de ces destinations. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_destinations(PyObject *self, void *unused) +static PyObject *py_arch_instruction_set_flag(PyObject *self, PyObject *args) { - PyObject *result; /* Instance à retourner */ - GArchInstruction *instr; /* Version native */ - size_t count; /* Nombre de liens présents */ - size_t i; /* Boucle de parcours */ - const instr_link_t *dest; /* Destination des liens */ - PyObject *linked; /* Destination de lien Python */ - PyObject *type; /* Nature du lien en Python */ -#ifndef NDEBUG - int ret; /* Bilan d'une écriture d'arg. */ -#endif - -#define ARCH_INSTRUCTION_DESTINATIONS_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - destinations, py_arch_instruction, \ - "Provide the instructions list following the current instruction.\n" \ - "\n" \ - "Each item of the resulting tuple is a pair of" \ - " pychrysalide.arch.ArchInstruction instance and" \ - " pychrysalide.arch.ArchInstruction.InstructionLinkType value." \ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_SET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + set_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Add some flags to the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to apply to the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ ) + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_lock_dest(instr); + status = g_arch_instruction_set_flag(instr, flag); - count = g_arch_instruction_count_destinations(instr); + result = status ? Py_True : Py_False; + Py_INCREF(result); - result = PyTuple_New(count); + return result; - for (i = 0; i < count; i++) - { - dest = g_arch_instruction_get_destination(instr, i); +} - linked = pygobject_new(G_OBJECT(dest->linked)); - type = cast_with_constants_group_from_type(get_python_arch_instruction_type(), - "InstructionLinkType", dest->type); -#ifndef NDEBUG - ret = PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); - assert(ret == 0); -#else - PyTuple_SetItem(result, i, Py_BuildValue("(OO)", linked, type)); -#endif +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Retire une information complémentaire à une instruction. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_instruction_unset_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + unset_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Remove some flags from the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to delete from the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) - unref_instr_link(dest); + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; - } + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_unlock_dest(instr); + status = g_arch_instruction_unset_flag(instr, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); return result; } +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Détermine si une instruction possède un fanion particulier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ -/* ---------------------------------------------------------------------------------- */ -/* INSTRUCTIONS D'ARCHITECTURES EN PYTHON */ -/* ---------------------------------------------------------------------------------- */ +static PyObject *py_arch_instruction_has_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchInstruction *instr; /* Instruction manipulée */ + bool status; /* Bilan à transmettre */ + +#define ARCH_INSTRUCTION_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + has_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_instruction, \ + "Tell if some flags are set for the instruction.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to test for the instruction state.\n" \ + "\n" \ + "The result is an boolean status: *True* if the bits" \ + " are active, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); + + status = g_arch_instruction_has_flag(instr, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* closure = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit l'identifiant unique pour un ensemble d'instructions.* +* Description : Fournit l'identifiant correspondant à un type d'instructions.* * * -* Retour : Identifiant unique par type d'instruction. * +* Retour : Identifiant unique par type d'instruction et architecture. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_unique_id(PyObject *self, void *closure) +static PyObject *py_arch_instruction_get_type_id(PyObject *self, void *closure) { - PyObject *result; /* Conversion à retourner */ - GArchInstruction *instr; /* Version native */ - itid_t uid; /* Identifiant unique associé */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + itid_t tid; /* Identifiant à transmettre */ + +#define ARCH_INSTRUCTION_TYPE_ID_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + type_id, py_arch_instruction, \ + "Provide the unique identifier given to this kind of" \ + " instruction.\n" \ + "\n" \ + "The returned value is an integer." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - uid = g_arch_instruction_get_unique_id(instr); + tid = g_arch_instruction_get_type_id(instr); - result = PyLong_FromUnsignedLong(uid); + result = PyLong_FromUnsignedLong(tid); return result; @@ -697,27 +1013,61 @@ static PyObject *py_arch_instruction_get_unique_id(PyObject *self, void *closure /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* closure = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit la place mémoire d'une instruction. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) +static PyObject *py_arch_instruction_get_encoding(PyObject *self, void *closure) { - PyObject *result; /* Conversion à retourner */ - GArchInstruction *instr; /* Version native */ - const mrange_t *range; /* Espace mémoire à exporter */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + char *encoding; /* Encodage d'une instruction */ + +#define ARCH_INSTRUCTION_ENCODING_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + encoding, py_arch_instruction, \ + "Describe the encoding related to an instruction.\n" \ + "\n" \ + "The returned value is an arbitrary string value." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - range = g_arch_instruction_get_range(instr); - result = build_from_internal_mrange(range); + encoding = g_arch_instruction_get_encoding(instr); + + if (encoding != NULL) + { + result = PyUnicode_FromString(encoding); + + free(encoding); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as encoding")); + + result = NULL; + + } return result; @@ -727,59 +1077,108 @@ static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) /****************************************************************************** * * * Paramètres : self = objet Python concerné par l'appel. * -* value = valeur fournie à intégrer ou prendre en compte. * -* closure = adresse non utilisée ici. * +* closure = non utilisé ici. * * * -* Description : Définit la localisation d'une instruction. * +* Description : Indique l'encodage d'une instruction de façon détaillée. * * * -* Retour : Bilan de l'opération pour Python. * +* Retour : Description humaine de l'encodage utilisé. * * * * Remarques : - * * * ******************************************************************************/ -static int py_arch_instruction_set_range(PyObject *self, PyObject *value, void *closure) +static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *closure) { - int ret; /* Bilan d'analyse */ - mrange_t *range; /* Espace mémoire à manipuler */ - GArchInstruction *instr; /* Version native */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + char *keyword; /* Désignation d'une instruct° */ + +#define ARCH_INSTRUCTION_KEYWORD_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + keyword, py_arch_instruction, \ + "Give the official name of the assembly instruction.\n" \ + "\n" \ + "The returned value is a string value." \ +) - ret = PyObject_IsInstance(value, (PyObject *)get_python_mrange_type()); - if (!ret) return -1; + instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - range = get_internal_mrange(value); + keyword = g_arch_instruction_get_keyword(instr); - instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - g_arch_instruction_set_range(instr, range); + if (keyword != NULL) + { + result = PyUnicode_FromString(keyword); - return 0; + free(keyword); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as keyword")); + + result = NULL; + + } + + return result; } /****************************************************************************** * * -* Paramètres : self = classe représentant une instruction. * -* unused = adresse non utilisée ici. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Fournit le nom humain de l'instruction manipulée. * +* Description : Fournit la place mémoire d'une instruction. * * * -* Retour : Valeur associée à la propriété consultée. * +* Retour : Définition de localisation ou *None*. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *unused) +static PyObject *py_arch_instruction_get_range(PyObject *self, void *closure) { - PyObject *result; /* Trouvailles à retourner */ - GArchInstruction *instr; /* Version native */ - const char *kw; /* Valeur récupérée */ + PyObject *result; /* Valeur à retourner */ + GArchInstruction *instr; /* Instruction manipulée */ + mrange_t range; /* Localisation d'instruction */ + bool valid; /* Validité de la localisation */ + +#define ARCH_INSTRUCTION_RANGE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + range, py_arch_instruction, \ + "Give access to the memory range covered by the" \ + " current instruction.\n" \ + "\n" \ + "The returned value is a pychrysalide.arch.mrange" \ + " instance or *None* if no location is currently" \ + " defined." \ +) instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - kw = g_arch_instruction_get_keyword(instr); - result = PyUnicode_FromString(kw); + valid = g_arch_instruction_get_range(instr, &range); + + if (valid) + result = build_from_internal_mrange(&range); + else + { + result = Py_None; + Py_INCREF(result); + } return result; @@ -801,45 +1200,25 @@ static PyObject *py_arch_instruction_get_keyword(PyObject *self, void *unused) PyTypeObject *get_python_arch_instruction_type(void) { static PyMethodDef py_arch_instruction_methods[] = { - { - "attach_operand", py_arch_instruction_attach_extra_operand, - METH_VARARGS, - "attach_operand($self, op, /)\n--\n\nAdd a new operand to the instruction." - }, - { - "replace_operand", py_arch_instruction_replace_operand, - METH_VARARGS, - "replace_operand($self, old, new, /)\n--\n\nReplace an old instruction operand by a another one." - }, - { - "detach_operand", py_arch_instruction_detach_operand, - METH_VARARGS, - "detach_operand($self, target, /)\n--\n\nRemove an operand from the instruction." - }, + ARCH_INSTRUCTION_ATTACH_OPERAND_METHOD, + ARCH_INSTRUCTION_REPLACE_OPERAND_METHOD, + ARCH_INSTRUCTION_DETACH_OPERAND_METHOD, ARCH_INSTRUCTION_FIND_OPERAND_PATH_METHOD, ARCH_INSTRUCTION_GET_OPERAND_FROM_PATH_METHOD, + ARCH_INSTRUCTION_SET_FLAG_METHOD, + ARCH_INSTRUCTION_UNSET_FLAG_METHOD, + ARCH_INSTRUCTION_HAS_FLAG_METHOD, { NULL } }; static PyGetSetDef py_arch_instruction_getseters[] = { - { - "uid", py_arch_instruction_get_unique_id, NULL, - "Provide the unique identification number given to this kind of instruction.", NULL - }, - { - "range", py_arch_instruction_get_range, py_arch_instruction_set_range, - "Give access to the memory range covered by the current instruction.", NULL - }, - { - "keyword", (getter)py_arch_instruction_get_keyword, (setter)NULL, - "Give le name of the assembly instruction.", NULL - }, - { - "operands", (getter)py_arch_instruction_get_operands, (setter)NULL, - "Provide the list of instruction attached operands.", NULL - }, ARCH_INSTRUCTION_SOURCES_ATTRIB, ARCH_INSTRUCTION_DESTINATIONS_ATTRIB, + ARCH_INSTRUCTION_OPERANDS_ATTRIB, + ARCH_INSTRUCTION_TYPE_ID_ATTRIB, + ARCH_INSTRUCTION_ENCODING_ATTRIB, + ARCH_INSTRUCTION_KEYWORD_ATTRIB, + ARCH_INSTRUCTION_RANGE_ATTRIB, { NULL } }; @@ -852,7 +1231,7 @@ PyTypeObject *get_python_arch_instruction_type(void) .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide instruction for a given architecture.", + .tp_doc = ARCH_INSTRUCTION_DOC, .tp_methods = py_arch_instruction_methods, .tp_getset = py_arch_instruction_getseters, @@ -893,9 +1272,14 @@ bool ensure_python_arch_instruction_is_registered(void) dict = PyModule_GetDict(module); - if (!ensure_python_line_generator_is_registered()) + if (!ensure_python_thick_object_is_registered()) return false; + if (!ensure_python_serializable_object_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_INSTRUCTION, (PyGClassInitFunc)py_arch_instruction_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_INSTRUCTION, type)) return false; diff --git a/plugins/pychrysalide/arch/instructions/Makefile.am b/plugins/pychrysalide/arch/instructions/Makefile.am index 65efe42..29c2a45 100644 --- a/plugins/pychrysalide/arch/instructions/Makefile.am +++ b/plugins/pychrysalide/arch/instructions/Makefile.am @@ -7,7 +7,8 @@ libpychrysaarchinstructions_la_SOURCES = \ raw.h raw.c \ undefined.h undefined.c -libpychrysaarchinstructions_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaarchinstructions_la_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + $(TOOLKIT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/arch/instructions/constants.c b/plugins/pychrysalide/arch/instructions/constants.c index af7baa9..257c501 100644 --- a/plugins/pychrysalide/arch/instructions/constants.c +++ b/plugins/pychrysalide/arch/instructions/constants.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.c - ajout des constantes de base pour les instructions * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -112,3 +112,59 @@ bool define_undefined_instruction_constants(PyTypeObject *type) return result; } + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en constante ExpectedBehavior. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_undefined_expected_behavior(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + unsigned long value; /* Valeur récupérée */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ExpectedBehavior"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if (value > IEB_RESERVED) + { + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ExpectedBehavior"); + result = 0; + } + + else + *((InstrExpectedBehavior *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/arch/instructions/constants.h b/plugins/pychrysalide/arch/instructions/constants.h index 2f0c587..b6ef9a4 100644 --- a/plugins/pychrysalide/arch/instructions/constants.h +++ b/plugins/pychrysalide/arch/instructions/constants.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.h - prototypes pour l'ajout des constantes de base pour les instructions * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -37,6 +37,9 @@ bool define_raw_instruction_constants(PyTypeObject *); /* Définit les constantes liées aux comportements erratiques. */ bool define_undefined_instruction_constants(PyTypeObject *); +/* Tente de convertir en constante ExpectedBehavior. */ +int convert_to_undefined_expected_behavior(PyObject *, void *); + #endif /* _PLUGINS_PYCHRYSALIDE_ARCH_INSTRUCTIONS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/arch/instructions/raw.c b/plugins/pychrysalide/arch/instructions/raw.c index 7e58b96..ae730e8 100644 --- a/plugins/pychrysalide/arch/instructions/raw.c +++ b/plugins/pychrysalide/arch/instructions/raw.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * raw.c - équivalent Python du fichier "arch/instructions/raw.h" * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,26 +28,33 @@ #include <pygobject.h> -#include <i18n.h> -#include <arch/instructions/raw.h> -#include <plugins/dt.h> +#include <arch/instructions/raw-int.h> #include "constants.h" #include "../instruction.h" #include "../vmpa.h" #include "../../access.h" +#include "../../constants.h" #include "../../helpers.h" #include "../../analysis/content.h" +#include "../../glibext/portion.h" -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_raw_instruction_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(raw_instruction, G_TYPE_RAW_INSTRUCTION); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_raw_instruction_init(PyObject *, PyObject *, PyObject *); + + +/* ------------------------ FONCTIONNALITES DE L'INSTRUCTION ------------------------ */ + + /* Indique si le contenu de l'instruction est du bourrage. */ static PyObject *py_raw_instruction_get_padding(PyObject *, void *); @@ -62,64 +69,9 @@ static int py_raw_instruction_set_string(PyObject *, PyObject *, void *); -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_raw_instruction_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - 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 */ - - /* Validations diverses */ - - base = get_python_raw_instruction_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_RAW_INSTRUCTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -139,20 +91,17 @@ static PyObject *py_raw_instruction_new(PyTypeObject *type, PyObject *args, PyOb static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { int result; /* Bilan à retourner */ + GBinaryPortion *area; /* Zone de contenance */ vmpa2t *addr; /* Texte de lecture */ - unsigned long mem_size; /* Taille de portion brute */ + MemoryDataSize size; /* Taille de portion brute */ unsigned long long value; /* Valeur brute à considérer */ GBinContent *content; /* Contenu à lire au besoin */ unsigned long count; /* Nombre d'éléments à lister */ - unsigned int endian; /* Type de boutisme impliqué */ + SourceEndian endian; /* Type de boutisme impliqué */ int ret; /* Bilan de lecture des args. */ - GArchInstruction *fake; /* Instruction à copier */ - GArchInstruction *instr; /* Instruction à manipuler */ - size_t op_count; /* Nombre d'opérande à copier */ - size_t i; /* Boucle de parcours */ - GArchOperand *op; /* Opérande à transférer */ + GRawInstruction *instr; /* Instruction à manipuler */ - static char *kwlist[] = { "addr", "mem_size", "value", "content", "count", "endian", NULL }; + static char *kwlist[] = { "area", "addr", "size", "value", "content", "count", "endian", NULL }; #define RAW_INSTRUCTION_DOC \ "The RawInstruction object handles data which is not (yet?) disassembled" \ @@ -187,9 +136,14 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd count = 0; endian = 0; - ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&k|KO&kI", kwlist, - convert_any_to_vmpa, &addr, &mem_size, - &value, convert_to_binary_content, &content, &count, &endian); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&O&O&|KO&kO&", kwlist, + convert_to_binary_portion, &area, + convert_any_to_vmpa, &addr, + convert_to_memory_data_size, &size, + &value, + convert_to_binary_content, &content, + &count, + convert_to_source_endian, &endian); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -199,35 +153,19 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd /* Eléments de base */ - if (content != NULL) - fake = g_raw_instruction_new_array(content, mem_size, count, addr, endian); - else - fake = g_raw_instruction_new_from_value(addr, mem_size, value); + instr = G_RAW_INSTRUCTION(pygobject_get(self)); - if (fake == NULL) + if (content != NULL) { - PyErr_SetString(PyExc_ValueError, _("Unable to build the object with the given parameters.")); - goto clean_exit; + if (!g_raw_instruction_create_array(instr, area, addr, size, content, count, endian)) + goto clean_exit; } - - instr = G_ARCH_INSTRUCTION(pygobject_get(self)); - - g_arch_instruction_lock_operands(fake); - - op_count = _g_arch_instruction_count_operands(fake); - - for (i = 0; i < op_count; i++) + else { - op = _g_arch_instruction_get_operand(fake, i); - g_arch_instruction_attach_extra_operand(instr, op); + if (!g_raw_instruction_create_value(instr, area, addr, size, value)) + goto clean_exit; } - g_arch_instruction_unlock_operands(fake); - - g_arch_instruction_set_range(instr, g_arch_instruction_get_range(fake)); - - g_object_unref(G_OBJECT(fake)); - result = 0; clean_exit: @@ -239,6 +177,12 @@ static int py_raw_instruction_init(PyObject *self, PyObject *args, PyObject *kwd } + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE L'INSTRUCTION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : self = classe représentant une instruction. * @@ -271,7 +215,6 @@ static PyObject *py_raw_instruction_get_padding(PyObject *self, void *closure) result = state ? Py_True : Py_False; Py_INCREF(result); - return result; } @@ -342,7 +285,6 @@ static PyObject *py_raw_instruction_get_string(PyObject *self, void *closure) result = state ? Py_True : Py_False; Py_INCREF(result); - return result; } diff --git a/plugins/pychrysalide/arch/instructions/undefined.c b/plugins/pychrysalide/arch/instructions/undefined.c index 1246daa..1c2bccc 100644 --- a/plugins/pychrysalide/arch/instructions/undefined.c +++ b/plugins/pychrysalide/arch/instructions/undefined.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.c - équivalent Python du fichier "arch/instructions/undefined.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,9 +28,7 @@ #include <pygobject.h> -#include <i18n.h> #include <arch/instructions/undefined-int.h> -#include <plugins/dt.h> #include "constants.h" @@ -40,75 +38,27 @@ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_undef_instruction_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Initialise une instance sur la base du dérivé de GObject. */ -static int py_undef_instruction_init(PyObject *, PyObject *, PyObject *); - -/* Indique le type de conséquences réél de l'instruction. */ -static PyObject *py_undef_instruction_get_behavior(PyObject *, void *); - - - -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_undef_instruction_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - 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 */ - - /* Validations diverses */ - base = get_python_undefined_instruction_type(); +CREATE_DYN_CONSTRUCTOR(undefined_instruction, G_TYPE_UNDEFINED_INSTRUCTION); - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_UNDEF_INSTRUCTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_undefined_instruction_init(PyObject *, PyObject *, PyObject *); - if (!status) - { - result = NULL; - goto exit; - } - } - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ +/* ------------------------ FONCTIONNALITES DE L'INSTRUCTION ------------------------ */ - simple_way: - result = PyType_GenericNew(type, args, kwds); +/* Indique le type de conséquences réél de l'instruction. */ +static PyObject *py_undefined_instruction_get_behavior(PyObject *, void *); - exit: - return result; -} +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -125,30 +75,27 @@ static PyObject *py_undef_instruction_new(PyTypeObject *type, PyObject *args, Py * * ******************************************************************************/ -static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) +static int py_undefined_instruction_init(PyObject *self, PyObject *args, PyObject *kwds) { - unsigned long behavior; /* Conséquence pour l'instruct°*/ + InstrExpectedBehavior behavior; /* Conséquence pour l'instruct°*/ int ret; /* Bilan de lecture des args. */ - GUndefInstruction *instr; /* Instruction à manipuler */ - undef_extra_data_t *extra; /* Données insérées à modifier */ + GUndefinedInstruction *instr; /* Instruction à manipuler */ - static char *kwlist[] = { "behavior", NULL }; - -#define UNDEF_INSTRUCTION_DOC \ - "UndefInstruction represents all kinds of instructions which are" \ +#define UNDEFINED_INSTRUCTION_DOC \ + "UndefinedInstruction represents all kinds of instructions which are" \ " officially not part of a runnable instruction set.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " UndefInstruction(behavior)" \ + " UndefinedInstruction(behavior)" \ "\n" \ "Where behavior is a" \ - " pychrysalide.arch.instructions.UndefInstruction.ExpectedBehavior" \ + " pychrysalide.arch.instructions.UndefinedInstruction.ExpectedBehavior" \ " constant describing the state of the CPU once the instruction is run." /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "k", kwlist, &behavior); + ret = PyArg_ParseTuple(args, "O&", convert_to_undefined_expected_behavior, &behavior); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -158,17 +105,22 @@ static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *k /* Eléments de base */ - instr = G_UNDEF_INSTRUCTION(pygobject_get(self)); - - extra = GET_UNDEF_INSTR_EXTRA(instr); + instr = G_UNDEFINED_INSTRUCTION(pygobject_get(self)); - extra->behavior = behavior; + if (!g_undefined_instruction_create(instr, behavior)) + return -1; return 0; } + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE L'INSTRUCTION */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : self = classe représentant une instruction. * @@ -182,24 +134,25 @@ static int py_undef_instruction_init(PyObject *self, PyObject *args, PyObject *k * * ******************************************************************************/ -static PyObject *py_undef_instruction_get_behavior(PyObject *self, void *closure) +static PyObject *py_undefined_instruction_get_behavior(PyObject *self, void *closure) { PyObject *result; /* Conversion à retourner */ - GUndefInstruction *instr; /* Version native */ + GUndefinedInstruction *instr; /* Version native */ InstrExpectedBehavior behavior; /* Comportement attendu */ -#define UNDEF_INSTRUCTION_BEHAVIOR_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - behavior, py_undef_instruction, \ - "Consequence carried by the undefined instruction.\n" \ - "\n" \ - "The result is provided as a" \ - " pychrysalide.arch.instructions.UndefInstruction.ExpectedBehavior" \ - " constant." \ +#define UNDEFINED_INSTRUCTION_BEHAVIOR_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + behavior, py_undefined_instruction, \ + "Consequence carried by the undefined instruction.\n" \ + "\n" \ + "The result is provided as a" \ + " pychrysalide.arch.instructions.UndefinedInstruction.ExpectedBehavior" \ + " constant." \ ) - instr = G_UNDEF_INSTRUCTION(pygobject_get(self)); - behavior = g_undef_instruction_get_behavior(instr); + instr = G_UNDEFINED_INSTRUCTION(pygobject_get(self)); + + behavior = g_undefined_instruction_get_behavior(instr); result = cast_with_constants_group_from_type(get_python_undefined_instruction_type(), "ExpectedBehavior", behavior); @@ -228,7 +181,7 @@ PyTypeObject *get_python_undefined_instruction_type(void) }; static PyGetSetDef py_undefined_instruction_getseters[] = { - UNDEF_INSTRUCTION_BEHAVIOR_ATTRIB, + UNDEFINED_INSTRUCTION_BEHAVIOR_ATTRIB, { NULL } }; @@ -236,18 +189,18 @@ PyTypeObject *get_python_undefined_instruction_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.instructions.UndefInstruction", + .tp_name = "pychrysalide.arch.instructions.UndefinedInstruction", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = UNDEF_INSTRUCTION_DOC, + .tp_doc = UNDEFINED_INSTRUCTION_DOC, .tp_methods = py_undefined_instruction_methods, .tp_getset = py_undefined_instruction_getseters, - .tp_init = py_undef_instruction_init, - .tp_new = py_undef_instruction_new, + .tp_init = py_undefined_instruction_init, + .tp_new = py_undefined_instruction_new, }; @@ -260,7 +213,7 @@ PyTypeObject *get_python_undefined_instruction_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.....UndefInstruction'. * +* Description : Prend en charge l'objet '....UndefinedInstruction'. * * * * Retour : Bilan de l'opération. * * * @@ -285,7 +238,7 @@ bool ensure_python_undefined_instruction_is_registered(void) if (!ensure_python_arch_instruction_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_UNDEF_INSTRUCTION, type)) + if (!register_class_for_pygobject(dict, G_TYPE_UNDEFINED_INSTRUCTION, type)) return false; if (!define_undefined_instruction_constants(type)) @@ -329,7 +282,7 @@ int convert_to_undefined_instruction(PyObject *arg, void *dst) break; case 1: - *((GUndefInstruction **)dst) = G_UNDEF_INSTRUCTION(pygobject_get(arg)); + *((GUndefinedInstruction **)dst) = G_UNDEFINED_INSTRUCTION(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/instructions/undefined.h b/plugins/pychrysalide/arch/instructions/undefined.h index 3fa0453..1453612 100644 --- a/plugins/pychrysalide/arch/instructions/undefined.h +++ b/plugins/pychrysalide/arch/instructions/undefined.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * undefined.h - prototypes pour l'équivalent Python du fichier "arch/instructions/undefined.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_undefined_instruction_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.instructions.UndefInstruction'. */ +/* Prend en charge l'objet 'pychrysalide.arch.instructions.UndefinedInstruction'. */ bool ensure_python_undefined_instruction_is_registered(void); /* Tente de convertir en instruction non définie. */ diff --git a/plugins/pychrysalide/arch/module-ui.c b/plugins/pychrysalide/arch/module-ui.c new file mode 100644 index 0000000..65d1290 --- /dev/null +++ b/plugins/pychrysalide/arch/module-ui.c @@ -0,0 +1,66 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire arch (forme graphique) en tant que module + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module-ui.h" + + +#include <assert.h> + + +#include "operand-ui.h" +#include "../glibext/generator.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'arch' (mode UI). * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_arch_module_ui(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_arch_operand_ui_is_registered(); + + /** + * Préparation du terrain pour les instructions, sans lien directe + * de la partie UI depuis la partie NOX. + */ + if (result) result = ensure_python_token_generator_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/constants.h b/plugins/pychrysalide/arch/module-ui.h index 56612c9..afa31d2 100644 --- a/plugins/pychrysalide/plugins/constants.h +++ b/plugins/pychrysalide/arch/module-ui.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * constants.h - prototypes pour l'ajout des constantes principales + * module.h - prototypes pour l'intégration du répertoire arch (forme graphique) en tant que module * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,17 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H +#ifndef _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H +#define _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H #include <Python.h> #include <stdbool.h> -/* Définit les constantes relatives aux greffons Python. */ -bool define_plugin_module_constants(PyTypeObject *); +/* Intègre les objets du module 'arch' (mode UI). */ +bool populate_arch_module_ui(void); -/* Tente de convertir en constante PluginAction. */ -int convert_to_plugin_action(PyObject *, void *); - -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_ARCH_MODULE_UI_H */ diff --git a/plugins/pychrysalide/arch/module.c b/plugins/pychrysalide/arch/module.c index bbdaf76..94f5ad7 100644 --- a/plugins/pychrysalide/arch/module.c +++ b/plugins/pychrysalide/arch/module.c @@ -28,22 +28,17 @@ #include <assert.h> -#include <arch/archbase.h> - - /* #include "context.h" #include "instriter.h" +*/ #include "instruction.h" #include "operand.h" -#include "processor.h" +//#include "processor.h" #include "register.h" -*/ #include "vmpa.h" -/* #include "instructions/module.h" #include "operands/module.h" -*/ #include "../helpers.h" @@ -80,10 +75,8 @@ bool add_arch_module(PyObject *super) result = (module != NULL); - /* if (result) result = add_arch_instructions_module(module); if (result) result = add_arch_operands_module(module); - */ return result; @@ -111,18 +104,16 @@ bool populate_arch_module(void) /* if (result) result = ensure_python_proc_context_is_registered(); if (result) result = ensure_python_instr_iterator_is_registered(); + */ if (result) result = ensure_python_arch_instruction_is_registered(); if (result) result = ensure_python_arch_operand_is_registered(); - if (result) result = ensure_python_arch_processor_is_registered(); + //if (result) result = ensure_python_arch_processor_is_registered(); if (result) result = ensure_python_arch_register_is_registered(); - */ if (result) result = ensure_python_vmpa_is_registered(); if (result) result = ensure_python_mrange_is_registered(); - /* if (result) result = populate_arch_instructions_module(); if (result) result = populate_arch_operands_module(); - */ assert(result); diff --git a/plugins/pychrysalide/arch/operand-ui.c b/plugins/pychrysalide/arch/operand-ui.c new file mode 100644 index 0000000..5062513 --- /dev/null +++ b/plugins/pychrysalide/arch/operand-ui.c @@ -0,0 +1,461 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.c - équivalent Python du fichier "arch/operand-ui.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "operand-ui.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <arch/operand-ui-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_arch_operand_ui_interface_init(GArchOperandUIInterface *, gpointer *); + +/* Traduit un opérande en version humainement lisible. */ +static void py_arch_operand_ui_print_wrapper(const GArchOperandUI *, GBufferLine *); + +/* Construit un petit résumé concis de l'opérande. */ +static char *py_arch_operand_ui_build_tooltip_wrapper(const GArchOperandUI *, const GLoadedBinary *); + +/* Traduit un opérande en version humainement lisible. */ +static PyObject *py_arch_operand_ui_print(PyObject *, PyObject *); + +/* Construit un petit résumé concis de l'opérande. */ +static PyObject *py_arch_operand_ui_build_tooltip(PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_arch_operand_ui_interface_init(GArchOperandUIInterface *iface, gpointer *unused) +{ +#define ARCH_OPERAND_UI_DOC \ + "The ArchOperandUI interface ensure pychrysalide.arch.ArchOperand" \ + " implementations provide UI features when Chrysalide is running with" \ + " a GUI.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(ArchOperand, ArchOperandUi):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.arch.ArchOperandUI._print();\n" \ + "* pychrysalide.arch.ArchOperandUI._build_tooltip().\n" + + iface->print = py_arch_operand_ui_print_wrapper; + iface->build_tooltip = py_arch_operand_ui_build_tooltip_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : operand = registre visé par la procédure. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_arch_operand_ui_print_wrapper(const GArchOperandUI *operand, GBufferLine *line) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define ARCH_OPERAND_UI_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _print, "$self, line, /", \ + METH_VARARGS, \ + "Abstract method used to print an operand into a rendering" \ + " *line*, which is a provided pychrysalide.glibext.BufferLine" \ + " instance." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(operand)); + + if (has_python_method(pyobj, "_print")) + { + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); + + pyret = run_python_method(pyobj, "_print", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : operand = opérande à consulter. * +* binary = informations relatives au binaire chargé. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_arch_operand_ui_build_tooltip_wrapper(const GArchOperandUI *operand, const GLoadedBinary *binary) +{ + char *result; /* Description à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define ARCH_OPERAND_UI_BUILD_TOOLTIP_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _build_tooltip, "$self, binary, /", \ + METH_VARARGS, \ + "Abstract method used to build a tooltip text shown when the" \ + " mouse is over an operand.\n" \ + "\n" \ + "A pychrysalide.analysis.LoadedBinary instance is provided in" \ + " case of need." \ +) + + result = NULL; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(operand)); + + if (has_python_method(pyobj, "_build_tooltip")) + { + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(binary))); + + pyret = run_python_method(pyobj, "_build_tooltip", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + result = strdup(PyUnicode_AsUTF8(pyret)); + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Traduit un opérande en version humainement lisible. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_ui_print(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + +#if 0 // TODO + + GBufferLine *line; /* Zone d'impression du rendu */ + int ret; /* Bilan de lecture des args. */ + GArchOperandUI *operand; /* Mécanismes natifs */ + +#define ARCH_OPERAND_UI_PRINT_METHOD PYTHON_METHOD_DEF \ +( \ + print, "$self, line", \ + METH_VARARGS, py_arch_operand_ui, \ + "Translate an operand into a human readable version.\n" \ + "\n" \ + "The *line* arguement is a pychrysalide.glibext.BufferLine" \ + " instance which has to get filled with rendering" \ + " information.\n" \ + "\n" \ + "The result returns nothing (*None*)." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND_UI(pygobject_get(self)); + + g_arch_operand_ui_print(operand, line); + +#endif + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Construit un petit résumé concis de l'opérande. * +* * +* Retour : Chaîne de caractères à libérer après usage ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_ui_build_tooltip(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + +#if 0 // TODO + + GLoadedBinary *binary; /* Infos sur le binaire chargé */ + int ret; /* Bilan de lecture des args. */ + GArchOperandUI *operand; /* Mécanismes natifs */ + char *tooltip; /* Eventuelle indication */ + +#define ARCH_OPERAND_UI_BUILD_TOOLTIP_METHOD PYTHON_METHOD_DEF \ +( \ + build_tooltip, "$self, binary", \ + METH_VARARGS, py_arch_operand_ui, \ + "Build a tooltip text shown when the mouse is over an" \ + " operand.\n" \ + "\n" \ + "The *binary* argument is a pychrysalide.analysis.LoadedBinary" \ + " instance provided in case of need." \ + "\n" \ + "The result is a string or *None* in case of error." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_loaded_binary, &binary); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND_UI(pygobject_get(self)); + + tooltip = g_arch_operand_ui_build_tooltip(operand, binary); + + if (tooltip != NULL) + { + PyUnicode_FromString(tooltip); + free(tooltip); + } + else + +#endif + + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_arch_operand_ui_type(void) +{ + static PyMethodDef py_arch_operand_ui_methods[] = { + ARCH_OPERAND_UI_PRINT_WRAPPER, + ARCH_OPERAND_UI_BUILD_TOOLTIP_WRAPPER, +#if 0 // TODO + ARCH_OPERAND_UI_PRINT_METHOD, + ARCH_OPERAND_UI_BUILD_TOOLTIP_METHOD, +#endif + { NULL } + }; + + static PyGetSetDef py_arch_operand_ui_getseters[] = { + { NULL } + }; + + static PyTypeObject py_arch_operand_ui_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.arch.ArchOperandUI", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = ARCH_OPERAND_UI_DOC, + + .tp_methods = py_arch_operand_ui_methods, + .tp_getset = py_arch_operand_ui_getseters + + }; + + return &py_arch_operand_ui_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.arch.ArchOperandUI'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_arch_operand_ui_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ArchOperandUI' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_arch_operand_ui_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_arch_operand_ui_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.arch"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_ARCH_OPERAND_UI, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'exportation graphique. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_arch_operand_ui(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_arch_operand_ui_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to UI arch operand"); + break; + + case 1: + *((GArchOperandUI **)dst) = G_ARCH_OPERAND_UI(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/arch/operand-ui.h b/plugins/pychrysalide/arch/operand-ui.h new file mode 100644 index 0000000..b9e2131 --- /dev/null +++ b/plugins/pychrysalide/arch/operand-ui.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * operand-ui.h - prototypes pour l'équivalent Python du fichier "arch/operand-ui.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H +#define _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_arch_operand_ui_type(void); + +/* Prend en charge l'objet 'pychrysalide.arch.ArchOperandUI'. */ +bool ensure_python_arch_operand_ui_is_registered(void); + +/* Tente de convertir en interface d'exportation graphique. */ +int convert_to_arch_operand_ui(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ARCH_OPERAND_UI_H */ diff --git a/plugins/pychrysalide/arch/operand.c b/plugins/pychrysalide/arch/operand.c index 0aee4f7..2281dae 100644 --- a/plugins/pychrysalide/arch/operand.c +++ b/plugins/pychrysalide/arch/operand.c @@ -30,23 +30,32 @@ #include <i18n.h> #include <arch/operand-int.h> -#include <plugins/dt.h> +#include <glibext/strbuilder-int.h> #include "../access.h" #include "../helpers.h" +#include "../glibext/comparable.h" +#include "../glibext/hashable.h" +#include "../glibext/objhole.h" +#include "../glibext/serialize.h" #include "../glibext/singleton.h" +#include "../glibext/strbuilder.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_operand_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe générique des opérandes. */ -static void py_arch_operand_init_gclass(GArchOperandClass *, gpointer); +static int py_arch_operand_init_gclass(GArchOperandClass *, PyTypeObject *); + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_operand, G_TYPE_ARCH_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_arch_operand_init(PyObject *, PyObject *, PyObject *); + +#if 0 /* Compare un opérande avec un autre. */ static int py_arch_operand___cmp___wrapper(const GArchOperand *, const GArchOperand *, bool); @@ -57,14 +66,6 @@ static char *py_arch_operand_find_inner_operand_path_wrapper(const GArchOperand /* Obtient l'opérande correspondant à un chemin donné. */ static GArchOperand *py_arch_operand_get_inner_operand_from_path_wrapper(const GArchOperand *, const char *); -/* Traduit un opérande en version humainement lisible. */ -static void py_arch_operand_print_wrapper(const GArchOperand *, GBufferLine *); - -#ifdef INCLUDE_GTK_SUPPORT - -/* Construit un petit résumé concis de l'opérande. */ -static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *, const GLoadedBinary *); - #endif @@ -72,6 +73,9 @@ static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *, const G /* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ +#if 0 + + /* Effectue une comparaison avec un objet Python 'ArchOperand'. */ static PyObject *py_arch_operand_richcompare(PyObject *, PyObject *, int); @@ -81,6 +85,19 @@ static PyObject *py_arch_operand_find_inner_operand_path(PyObject *, PyObject *) /* Obtient l'opérande correspondant à un chemin donné. */ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *, PyObject *); +#endif + + +/* Ajoute une information complémentaire à un opérande. */ +static PyObject *py_arch_operand_set_flag(PyObject *, PyObject *); + +/* Retire une information complémentaire à un opérande. */ +static PyObject *py_arch_operand_unset_flag(PyObject *, PyObject *); + +/* Détermine si un opérande possède un fanion particulier. */ +static PyObject *py_arch_operand_has_flag(PyObject *, PyObject *); + + /* ---------------------------------------------------------------------------------- */ @@ -90,113 +107,109 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *, PyObjec /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise la classe générique des opérandes. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_arch_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_arch_operand_init_gclass(GArchOperandClass *gclass, PyTypeObject *pyclass) { - 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 */ - -#define ARCH_OPERAND_DOC \ - "The ArchOperand object aims to get subclassed to create" \ - " operands of any kind for new architectures.\n" \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect"\ - " no particular argument.\n" \ - "\n" \ - "The following methods have to be defined for new classes:\n" \ - "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ - "* pychrysalide.arch.ArchRegister._print();\n" \ - "* pychrysalide.arch.ArchRegister._build_tooltip().\n" \ - "\n" \ - "Some extra method definitions are optional for new classes:\n" \ - "* pychrysalide.arch.ArchRegister._find_inner_operand_path();\n" \ - "* pychrysalide.arch.ArchRegister._get_inner_operand_from_path().\n"\ - "\n" \ - "Chrysalide creates an internal glue to provide rich comparisons" \ - " for operands based on the old-style *__cmp__* function." - /* Validations diverses */ +#if 0 + GStringBuilderInterface *iface; /* Interface utilisée */ - base = get_python_arch_operand_type(); + iface = g_type_interface_peek(gclass, G_TYPE_STRING_BUILDER); - 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é */ + /* + printf("???????? init Python Operand ?????????????? -> class: %p '%s' - strbuilder iface: %p\n", + gclass, g_type_name(G_TYPE_FROM_CLASS(gclass)), iface); + */ - first_time = (g_type_from_name(type->tp_name) == 0); +#endif - gtype = build_dynamic_type(G_TYPE_ARCH_OPERAND, type->tp_name, - (GClassInitFunc)py_arch_operand_init_gclass, NULL, NULL); +#if 0 - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); + class->compare = py_arch_operand___cmp___wrapper; + class->find_inner = py_arch_operand_find_inner_operand_path_wrapper; + class->get_inner = py_arch_operand_get_inner_operand_from_path_wrapper; - if (!status) - { - result = NULL; - goto exit; - } - } +#endif - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - result = PyType_GenericNew(type, args, kwds); - exit: + //PY_CLASS_SET_WRAPPER(gclass->xxx, py_arch_operand_xxx_wrapper); - return result; + return 0; } /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Initialise la classe générique des opérandes. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_operand_init_gclass(GArchOperandClass *class, gpointer unused) +static int py_arch_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - class->compare = py_arch_operand___cmp___wrapper; - class->find_inner = py_arch_operand_find_inner_operand_path_wrapper; - class->get_inner = py_arch_operand_get_inner_operand_from_path_wrapper; + //unsigned int endianness; /* Boutisme du processeur */ + int ret; /* Bilan de lecture des args. */ + //GArchProcessor *proc; /* Processeur à manipuler */ - class->print = py_arch_operand_print_wrapper; -#ifdef INCLUDE_GTK_SUPPORT - class->build_tooltip = py_arch_operand_build_tooltip_wrapper; -#endif +#define ARCH_OPERAND_DOC \ + "The ArchOperand object aims to get subclassed to create" \ + " operands of any kind for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " no particular argument.\n" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ + "* pychrysalide.arch.ArchRegister._print();\n" \ + "* pychrysalide.arch.ArchRegister._build_tooltip().\n" \ + "\n" \ + "Some extra method definitions are optional for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._find_inner_operand_path();\n" \ + "* pychrysalide.arch.ArchRegister._get_inner_operand_from_path().\n"\ + "\n" \ + "Chrysalide creates an internal glue to provide rich comparisons" \ + " for operands based on the old-style *__cmp__* function." + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + //proc = G_ARCH_PROCESSOR(pygobject_get(self)); + + //proc->endianness = endianness; + + return 0; } + +#if 0 + /****************************************************************************** * * * Paramètres : a = premier opérande à consulter. * @@ -410,126 +423,6 @@ static GArchOperand *py_arch_operand_get_inner_operand_from_path_wrapper(const G } -/****************************************************************************** -* * -* Paramètres : operand = registre visé par la procédure. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_arch_operand_print_wrapper(const GArchOperand *operand, GBufferLine *line) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_OPERAND_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to print the operand into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(operand)); - - if (has_python_method(pyobj, "_print")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - - pyret = run_python_method(pyobj, "_print", args); - - Py_XDECREF(pyret); - - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -#ifdef INCLUDE_GTK_SUPPORT - - -/****************************************************************************** -* * -* Paramètres : operand = opérande à consulter. * -* binary = informations relatives au binaire chargé. * -* * -* Description : Construit un petit résumé concis de l'opérande. * -* * -* Retour : Chaîne de caractères à libérer après usage ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *operand, const GLoadedBinary *binary) -{ - char *result; /* Description à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_OPERAND_BUILD_TOOLTIP_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _build_tooltip, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to build a tooltip text shown when the" \ - " mouse is over the operand.\n" \ - "\n" \ - "A pychrysalide.analysis.LoadedBinary instance is provided in" \ - " case of need." \ -) - - result = NULL; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(operand)); - - if (has_python_method(pyobj, "_build_tooltip")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(binary))); - - pyret = run_python_method(pyobj, "_build_tooltip", args); - - if (pyret != NULL) - { - if (PyUnicode_Check(pyret)) - result = strdup(PyUnicode_AsUTF8(pyret)); - } - - Py_XDECREF(pyret); - - Py_DECREF(args); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - #endif @@ -540,6 +433,9 @@ static char *py_arch_operand_build_tooltip_wrapper(const GArchOperand *operand, /* ---------------------------------------------------------------------------------- */ + +#if 0 + /****************************************************************************** * * * Paramètres : a = premier object Python à consulter. * @@ -701,6 +597,164 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *self, PyO } +#endif + + + + + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Ajoute une information complémentaire à un opérande. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_set_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_SET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + set_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Add some flags to the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to apply to the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_set_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Retire une information complémentaire à un opérande. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_unset_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_UNSET_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + unset_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Remove some flags from the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to delete from the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* for operation" \ + " success, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_unset_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = architecture concernée par la procédure. * +* args = instruction représentant le point de départ. * +* * +* Description : Détermine si un opérande possède un fanion particulier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_arch_operand_has_flag(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int flag; /* Fanion(s) à appliquer */ + int ret; /* Bilan de lecture des args. */ + GArchOperand *operand; /* Opérande manipulé */ + bool status; /* Bilan à transmettre */ + +#define ARCH_OPERAND_HAS_FLAG_METHOD PYTHON_METHOD_DEF \ +( \ + has_flag, "$self, flag, /", \ + METH_VARARGS, py_arch_operand, \ + "Tell if some flags are set for the operand.\n" \ + "\n" \ + "This *flag* argument is an integer value containing" \ + " bits to test for the operand state.\n" \ + "\n" \ + "The result is an boolean status: *True* if the bits" \ + " are active, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "I", &flag); + if (!ret) return NULL; + + operand = G_ARCH_OPERAND(pygobject_get(self)); + + status = g_arch_operand_has_flag(operand, flag); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + /****************************************************************************** * * @@ -717,6 +771,11 @@ static PyObject *py_arch_operand_get_inner_operand_from_path(PyObject *self, PyO PyTypeObject *get_python_arch_operand_type(void) { static PyMethodDef py_arch_operand_methods[] = { + ARCH_OPERAND_SET_FLAG_METHOD, + ARCH_OPERAND_UNSET_FLAG_METHOD, + ARCH_OPERAND_HAS_FLAG_METHOD, + + /* ARCH_OPERAND_CMP_WRAPPER, ARCH_OPERAND_FIND_INNER_OPERAND_PATH_WRAPPER, ARCH_OPERAND_GET_INNER_OPERAND_FROM_PATH_WRAPPER, @@ -726,6 +785,7 @@ PyTypeObject *get_python_arch_operand_type(void) #endif ARCH_OPERAND_FIND_INNER_OPERAND_PATH_METHOD, ARCH_OPERAND_GET_INNER_OPERAND_FROM_PATH_METHOD, + */ { NULL } }; @@ -744,11 +804,12 @@ PyTypeObject *get_python_arch_operand_type(void) .tp_doc = ARCH_OPERAND_DOC, - .tp_richcompare = py_arch_operand_richcompare, + //.tp_richcompare = py_arch_operand_richcompare, .tp_methods = py_arch_operand_methods, .tp_getset = py_arch_operand_getseters, + .tp_init = py_arch_operand_init, .tp_new = py_arch_operand_new, }; @@ -784,9 +845,26 @@ bool ensure_python_arch_operand_is_registered(void) dict = PyModule_GetDict(module); + if (!ensure_python_thick_object_is_registered()) + return false; + + if (!ensure_python_comparable_object_is_registered()) + return false; + + if (!ensure_python_hashable_object_is_registered()) + return false; + + if (!ensure_python_serializable_object_is_registered()) + return false; + if (!ensure_python_singleton_candidate_is_registered()) return false; + if (!ensure_python_string_builder_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_OPERAND, (PyGClassInitFunc)py_arch_operand_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_OPERAND, type)) return false; diff --git a/plugins/pychrysalide/arch/operand.h b/plugins/pychrysalide/arch/operand.h index 9cb40a0..f3bfbf2 100644 --- a/plugins/pychrysalide/arch/operand.h +++ b/plugins/pychrysalide/arch/operand.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * operand.h - prototypes pour l'équivalent Python du fichier "arch/operand.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,9 +31,6 @@ -/* ------------------------ DEFINITION D'OPERANDE QUELCONQUE ------------------------ */ - - /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_arch_operand_type(void); diff --git a/plugins/pychrysalide/arch/operands/Makefile.am b/plugins/pychrysalide/arch/operands/Makefile.am index a41cbbb..3b753cc 100644 --- a/plugins/pychrysalide/arch/operands/Makefile.am +++ b/plugins/pychrysalide/arch/operands/Makefile.am @@ -1,19 +1,21 @@ noinst_LTLIBRARIES = libpychrysaarchoperands.la +# libpychrysaarchoperands_la_SOURCES = \ +# feeder.h feeder.c \ +# proxy.h proxy.c \ +# rename.h rename.c \ +# target.h target.c \ +# targetable.h targetable.c + libpychrysaarchoperands_la_SOURCES = \ constants.h constants.c \ - feeder.h feeder.c \ immediate.h immediate.c \ known.h known.c \ module.h module.c \ - proxy.h proxy.c \ - register.h register.c \ - rename.h rename.c \ - target.h target.c \ - targetable.h targetable.c + register.h register.c -libpychrysaarchoperands_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaarchoperands_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/arch/operands/constants.c b/plugins/pychrysalide/arch/operands/constants.c index b9d80e4..78eeded 100644 --- a/plugins/pychrysalide/arch/operands/constants.c +++ b/plugins/pychrysalide/arch/operands/constants.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.c - ajout des constantes de base pour les opérandes * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -47,20 +47,33 @@ * * ******************************************************************************/ -bool define_imm_operand_constants(PyTypeObject *type) +bool define_immediate_operand_constants(PyTypeObject *type) { bool result; /* Bilan à retourner */ PyObject *values; /* Groupe de valeurs à établir */ values = PyDict_New(); + result = add_const_to_group(values, "ZERO_PADDING_BY_DEFAULT", IOF_ZERO_PADDING_BY_DEFAULT); + if (result) result = add_const_to_group(values, "ZERO_PADDING", IOF_ZERO_PADDING); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, true, "ImmOperandFlag", values, + "Specific state bits for immediate operands."); + + values = PyDict_New(); + result = add_const_to_group(values, "BIN", IOD_BIN); if (result) result = add_const_to_group(values, "OCT", IOD_OCT); if (result) result = add_const_to_group(values, "DEC", IOD_DEC); if (result) result = add_const_to_group(values, "HEX", IOD_HEX); if (result) result = add_const_to_group(values, "CHAR", IOD_CHAR); if (result) result = add_const_to_group(values, "COUNT", IOD_COUNT); - if (result) result = add_const_to_group(values, "LAST_VALID", IOD_LAST_VALID); if (!result) { diff --git a/plugins/pychrysalide/arch/operands/constants.h b/plugins/pychrysalide/arch/operands/constants.h index 71a26cc..5170faa 100644 --- a/plugins/pychrysalide/arch/operands/constants.h +++ b/plugins/pychrysalide/arch/operands/constants.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * constants.h - prototypes pour l'ajout des constantes de base pour les opérandes * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,11 +32,14 @@ /* Définit les constantes relatives aux opérandes d'immédiats. */ -bool define_imm_operand_constants(PyTypeObject *); +bool define_immediate_operand_constants(PyTypeObject *); /* Tente de convertir en constante ImmOperandDisplay. */ int convert_to_imm_operand_display(PyObject *, void *); +#define cast_imm_operand_display_to_python(v) \ + cast_with_constants_group_from_type(get_python_immediate_operand_type(), "ImmOperandDisplay", v) + #endif /* _PLUGINS_PYCHRYSALIDE_ARCH_OPERANDS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/arch/operands/immediate.c b/plugins/pychrysalide/arch/operands/immediate.c index 2239eb2..a335db3 100644 --- a/plugins/pychrysalide/arch/operands/immediate.c +++ b/plugins/pychrysalide/arch/operands/immediate.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.c - équivalent Python du fichier "arch/operands/immediate.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,201 +30,119 @@ #include <i18n.h> - - -#include <arch/operands/immediate.h> +#include <arch/operands/immediate-int.h> #include "constants.h" -#include "rename.h" -#include "targetable.h" #include "../operand.h" #include "../../access.h" +#include "../../constants.h" #include "../../helpers.h" #include "../../analysis/content.h" -#include "../../glibext/bufferline.h" -/* Crée un nouvel objet Python de type 'ImmOperand'. */ -static PyObject *py_imm_operand_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(immediate_operand, G_TYPE_IMMEDIATE_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_immediate_operand_init(PyObject *, PyObject *, PyObject *); + + -/* Compare un opérande avec un autre. */ -static PyObject *py_imm_operand___cmp__(PyObject *, PyObject *); +/* ---------------------------- DEFINITION D'UN IMMEDIAT ---------------------------- */ -/* Traduit un opérande en version humainement lisible. */ -static PyObject *py_imm_operand__print(PyObject *, PyObject *); /* Renseigne la taille de la valeur indiquée à la construction. */ -static PyObject *py_imm_operand_get_size(PyObject *, void *); +static PyObject *py_immediate_operand_get_size(PyObject *, void *); /* Fournit la valeur portée par une opérande numérique. */ -static PyObject *py_imm_operand_get_value(PyObject *, void *); +static PyObject *py_immediate_operand_get_value(PyObject *, void *); + +/* Indique le signe d'une valeur immédiate. */ +static PyObject *py_immediate_operand_is_negative(PyObject *, void *); /* Indique le format textuel par défaut de la valeur. */ -static PyObject *py_imm_operand_get_default_display(PyObject *, void *); +static PyObject *py_immediate_operand_get_default_display(PyObject *, void *); /* Définit le format textuel par défaut de la valeur. */ -static int py_imm_operand_set_default_display(PyObject *, PyObject *, void *); +static int py_immediate_operand_set_default_display(PyObject *, PyObject *, void *); /* Indique la grande ligne du format textuel de la valeur. */ -static PyObject *py_imm_operand_get_display(PyObject *, void *); +static PyObject *py_immediate_operand_get_display(PyObject *, void *); /* Définit la grande ligne du format textuel de la valeur. */ -static int py_imm_operand_set_display(PyObject *, PyObject *, void *); +static int py_immediate_operand_set_display(PyObject *, PyObject *, void *); + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * +* Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * -* Description : Crée un nouvel objet Python de type 'ImmOperand'. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Instance Python mise en place. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_imm_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_immediate_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Instance à retourner */ - unsigned int raw_size; /* Taille obtenue de Python */ + MemoryDataSize size; /* Taille des données finale */ unsigned long long value; /* Valeur brute à représenter */ int ret; /* Bilan de lecture des args. */ - MemoryDataSize size; /* Taille des données finale */ - GArchOperand *operand; /* Création GLib à transmettre */ + GImmediateOperand *operand; /* Opérande natif à manipuler */ -#define IMM_OPERAND_DOC \ - "The ImmOperand deals with immediate value as operand." \ +#define IMMEDIATE_OPERAND_DOC \ + "The ImmediateOperand deals with immediate value as operand." \ "\n" \ "There are several ways to display these values in a disassembly," \ " the operand handles that.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " ImmOperand(size, value)" \ + " ImmediateOperand(size, value)" \ "\n" \ - "Where size specifies the original size of the provided value, as" \ - " a pychrysalide.analysis.BinContent.MemoryDataSize." + "Where *size* specifies the original size of the provided *value*," \ + " as a pychrysalide.MemoryDataSize." - ret = PyArg_ParseTuple(args, "IK", &raw_size, &value); - if (!ret) return NULL; + /* Récupération des paramètres */ - size = raw_size; + ret = PyArg_ParseTuple(args, "O&K", convert_to_memory_data_size, &size, &value); + if (!ret) return -1; - if (size != MDS_UNDEFINED - && !(MDS_4_BITS_UNSIGNED <= size && size <= MDS_64_BITS_UNSIGNED) - && !(MDS_4_BITS_SIGNED <= size && size <= MDS_64_BITS_SIGNED)) - { - PyErr_SetString(PyExc_ValueError, _("Invalid size to build an immediate operand")); - return NULL; - } + /* Initialisation d'un objet GLib */ - operand = g_imm_operand_new_from_value(size, value); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - result = pygobject_new(G_OBJECT(operand)); + /* Eléments de base */ - g_object_unref(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - return (PyObject *)result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_imm_operand___cmp__(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GImmOperand *other; /* Autre opérande à manipuler */ - int ret; /* Bilan de lecture des args. */ - GImmOperand *operand; /* Elément à manipuler */ - int status; /* Bilan de comparaison */ - -#define IMM_OPERAND_CMP_METHOD PYTHON_METHOD_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, py_imm_operand, \ - "Implementation of the required method used to compare the" \ - " operand with another one. This second object is always" \ - " an pychrysalide.arch.ImmOperand instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_imm_operand, &other); - if (!ret) return NULL; - - operand = G_IMM_OPERAND(pygobject_get(self)); - - status = g_arch_operand_compare(G_ARCH_OPERAND(operand), G_ARCH_OPERAND(other)); - - result = PyLong_FromLong(status); + if (!g_immediate_operand_create_from_value(operand, size, value)) + return -1; - return result; + return 0; } -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ -static PyObject *py_imm_operand__print(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GBufferLine *line; /* Ligne fournie à peupler */ - int ret; /* Bilan de lecture des args. */ - GImmOperand *operand; /* Elément à manipuler */ - -#define IMM_OPERAND_PRINT_METHOD PYTHON_METHOD_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, py_imm_operand, \ - "Implementation of the required method used to print the operand" \ - " into a rendering line, which is a provided" \ - " pychrysalide.glibext.BufferLine instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); - if (!ret) return NULL; - - operand = G_IMM_OPERAND(pygobject_get(self)); - - g_arch_operand_print(G_ARCH_OPERAND(operand), line); - - result = Py_None; - Py_INCREF(result); - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN IMMEDIAT */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -240,25 +158,26 @@ static PyObject *py_imm_operand__print(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_size(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_size(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ MemoryDataSize size; /* Type de donnée représentée */ -#define IMM_OPERAND_SIZE_ATTRIB PYTHON_GET_DEF_FULL \ +#define IMMEDIATE_OPERAND_SIZE_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - size, py_imm_operand, \ + size, py_immediate_operand, \ "Get or set the size of the value contained in the operand." \ "\n" \ "The property is a value of type" \ - " pychrysalide.analysis.BinContent.MemoryDataSize." \ + " pychrysalide.MemoryDataSize." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - size = g_imm_operand_get_size(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + size = g_immediate_operand_get_size(operand); - result = cast_with_constants_group_from_type(get_python_binary_content_type(), "MemoryDataSize", size); + result = cast_memory_data_size_to_python(size); return result; @@ -278,10 +197,10 @@ static PyObject *py_imm_operand_get_size(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_value(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ MemoryDataSize size; /* Type de donnée représentée */ uint8_t uval8; /* Valeur sur 8 bits */ uint16_t uval16; /* Valeur sur 16 bits */ @@ -292,15 +211,15 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) int32_t sval32; /* Valeur sur 32 bits */ int64_t sval64; /* Valeur sur 64 bits */ -#define IMM_OPERAND_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - value, py_imm_operand, \ - "Value of the immediate operand, as an integer." \ +#define IMMEDIATE_OPERAND_VALUE_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + value, py_immediate_operand, \ + "Value of the immediate operand, as an integer." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - size = g_imm_operand_get_size(operand); + size = g_immediate_operand_get_size(operand); switch (size) { @@ -311,36 +230,36 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) break; case MDS_4_BITS_UNSIGNED: case MDS_8_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval8); + g_immediate_operand_get_value(operand, size, &uval8); result = PyLong_FromUnsignedLong(uval8); break; case MDS_16_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval16); + g_immediate_operand_get_value(operand, size, &uval16); result = PyLong_FromUnsignedLong(uval16); break; case MDS_32_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval32); + g_immediate_operand_get_value(operand, size, &uval32); result = PyLong_FromUnsignedLong(uval32); break; case MDS_64_BITS_UNSIGNED: - g_imm_operand_get_value(operand, size, &uval64); + g_immediate_operand_get_value(operand, size, &uval64); result = PyLong_FromUnsignedLongLong(uval64); break; case MDS_4_BITS_SIGNED: case MDS_8_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval8); + g_immediate_operand_get_value(operand, size, &sval8); result = PyLong_FromLong(sval8); break; case MDS_16_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval16); + g_immediate_operand_get_value(operand, size, &sval16); result = PyLong_FromLong(sval16); break; case MDS_32_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval32); + g_immediate_operand_get_value(operand, size, &sval32); result = PyLong_FromLong(sval32); break; case MDS_64_BITS_SIGNED: - g_imm_operand_get_value(operand, size, &sval64); + g_immediate_operand_get_value(operand, size, &sval64); result = PyLong_FromLongLong(sval64); break; @@ -363,6 +282,43 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * +* Description : Indique le signe d'une valeur immédiate. * +* * +* Retour : True si la valeur est strictement négative, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_immediate_operand_is_negative(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ + bool status; /* Etat à faire connaître */ + +#define IMMEDIATE_OPERAND_NEGATIVE_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + negative, py_immediate_operand, \ + "Sign of the value, as a boolean status." \ +) + + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + status = g_immediate_operand_is_negative(operand); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * * Description : Indique le format textuel par défaut de la valeur. * * * * Retour : Format global d'un affichage de valeur. * @@ -371,25 +327,26 @@ static PyObject *py_imm_operand_get_value(PyObject *self, void *closure) * * ******************************************************************************/ -static PyObject *py_imm_operand_get_default_display(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_default_display(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ ImmOperandDisplay display; /* Type d'affichage courant */ -#define IMM_OPERAND_DEFAULT_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ +#define IMMEDIATE_OPERAND_DEFAULT_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ ( \ - default_display, py_imm_operand, \ + default_display, py_immediate_operand, \ "Define of the immediate operand default textual representation." \ "\n" \ "The property is a value of type" \ " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - display = g_imm_operand_get_default_display(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); + + display = g_immediate_operand_get_default_display(operand); - result = cast_with_constants_group_from_type(get_python_imm_operand_type(), "ImmOperandDisplay", display); + result = cast_imm_operand_display_to_python(display); return result; @@ -410,10 +367,10 @@ static PyObject *py_imm_operand_get_default_display(PyObject *self, void *closur * * ******************************************************************************/ -static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, void *closure) +static int py_immediate_operand_set_default_display(PyObject *self, PyObject *value, void *closure) { ImmOperandDisplay display; /* Type d'affichage demandé */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ if (!PyLong_Check(value)) { @@ -429,9 +386,9 @@ static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, v return -1; } - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - g_imm_operand_set_default_display(operand, display); + g_immediate_operand_set_default_display(operand, display); return 0; @@ -451,25 +408,27 @@ static int py_imm_operand_set_default_display(PyObject *self, PyObject *value, v * * ******************************************************************************/ -static PyObject *py_imm_operand_get_display(PyObject *self, void *closure) +static PyObject *py_immediate_operand_get_display(PyObject *self, void *closure) { PyObject *result; /* Instance Python à retourner */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ ImmOperandDisplay display; /* Type d'affichage courant */ -#define IMM_OPERAND_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ -( \ - display, py_imm_operand, \ - "Define of the immediate operand current textual representation." \ - "\n" \ - "The property is a value of type" \ - " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ +#define IMMEDIATE_OPERAND_DISPLAY_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + display, py_immediate_operand, \ + "Retrieve or define of the immediate operand current textual" \ + " representation." \ + "\n" \ + "The property is a value of type" \ + " pychrysalide.arch.operands.ImmOperand.ImmOperandDisplay." \ ) - operand = G_IMM_OPERAND(pygobject_get(self)); - display = g_imm_operand_get_display(operand); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - result = cast_with_constants_group_from_type(get_python_imm_operand_type(), "ImmOperandDisplay", display); + display = g_immediate_operand_get_display(operand); + + result = cast_imm_operand_display_to_python(display); return result; @@ -490,10 +449,10 @@ static PyObject *py_imm_operand_get_display(PyObject *self, void *closure) * * ******************************************************************************/ -static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *closure) +static int py_immediate_operand_set_display(PyObject *self, PyObject *value, void *closure) { ImmOperandDisplay display; /* Type d'affichage demandé */ - GImmOperand *operand; /* Version GLib de l'opérande */ + GImmediateOperand *operand; /* Version GLib de l'opérande */ if (!PyLong_Check(value)) { @@ -509,9 +468,9 @@ static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *clo return -1; } - operand = G_IMM_OPERAND(pygobject_get(self)); + operand = G_IMMEDIATE_OPERAND(pygobject_get(self)); - g_imm_operand_set_display(operand, display); + g_immediate_operand_set_display(operand, display); return 0; @@ -530,40 +489,41 @@ static int py_imm_operand_set_display(PyObject *self, PyObject *value, void *clo * * ******************************************************************************/ -PyTypeObject *get_python_imm_operand_type(void) +PyTypeObject *get_python_immediate_operand_type(void) { - static PyMethodDef py_imm_operand_methods[] = { - IMM_OPERAND_CMP_METHOD, - IMM_OPERAND_PRINT_METHOD, + static PyMethodDef py_immediate_operand_methods[] = { { NULL } }; - static PyGetSetDef py_imm_operand_getseters[] = { - IMM_OPERAND_SIZE_ATTRIB, - IMM_OPERAND_VALUE_ATTRIB, - IMM_OPERAND_DEFAULT_DISPLAY_ATTRIB, - IMM_OPERAND_DISPLAY_ATTRIB, + static PyGetSetDef py_immediate_operand_getseters[] = { + IMMEDIATE_OPERAND_SIZE_ATTRIB, + IMMEDIATE_OPERAND_VALUE_ATTRIB, + IMMEDIATE_OPERAND_NEGATIVE_ATTRIB, + IMMEDIATE_OPERAND_DEFAULT_DISPLAY_ATTRIB, + IMMEDIATE_OPERAND_DISPLAY_ATTRIB, { NULL } }; - static PyTypeObject py_imm_operand_type = { + static PyTypeObject py_immediate_operand_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.operands.ImmOperand", + .tp_name = "pychrysalide.arch.operands.ImmediateOperand", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = IMM_OPERAND_DOC, + .tp_doc = IMMEDIATE_OPERAND_DOC, + + .tp_methods = py_immediate_operand_methods, + .tp_getset = py_immediate_operand_getseters, - .tp_methods = py_imm_operand_methods, - .tp_getset = py_imm_operand_getseters, - .tp_new = py_imm_operand_new + .tp_init = py_immediate_operand_init, + .tp_new = py_immediate_operand_new, }; - return &py_imm_operand_type; + return &py_immediate_operand_type; } @@ -580,13 +540,13 @@ PyTypeObject *get_python_imm_operand_type(void) * * ******************************************************************************/ -bool ensure_python_imm_operand_is_registered(void) +bool ensure_python_immediate_operand_is_registered(void) { - PyTypeObject *type; /* Type Python 'ImmOperand' */ + PyTypeObject *type; /* Type 'ImmediateOperand' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - type = get_python_imm_operand_type(); + type = get_python_immediate_operand_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -597,16 +557,10 @@ bool ensure_python_imm_operand_is_registered(void) if (!ensure_python_arch_operand_is_registered()) return false; - if (!ensure_python_targetable_operand_is_registered()) - return false; - - if (!ensure_python_renameable_operand_is_registered()) - return false; - - if (!register_class_for_pygobject(dict, G_TYPE_IMM_OPERAND, type)) + if (!register_class_for_pygobject(dict, G_TYPE_IMMEDIATE_OPERAND, type)) return false; - if (!define_imm_operand_constants(type)) + if (!define_immediate_operand_constants(type)) return false; } @@ -629,11 +583,11 @@ bool ensure_python_imm_operand_is_registered(void) * * ******************************************************************************/ -int convert_to_imm_operand(PyObject *arg, void *dst) +int convert_to_immediate_operand(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_imm_operand_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_immediate_operand_type()); switch (result) { @@ -647,7 +601,7 @@ int convert_to_imm_operand(PyObject *arg, void *dst) break; case 1: - *((GImmOperand **)dst) = G_IMM_OPERAND(pygobject_get(arg)); + *((GImmediateOperand **)dst) = G_IMMEDIATE_OPERAND(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/operands/immediate.h b/plugins/pychrysalide/arch/operands/immediate.h index 4a1e6de..8b8de83 100644 --- a/plugins/pychrysalide/arch/operands/immediate.h +++ b/plugins/pychrysalide/arch/operands/immediate.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * immediate.h - prototypes pour l'équivalent Python du fichier "arch/operands/immediate.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,13 +32,13 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_imm_operand_type(void); +PyTypeObject *get_python_immediate_operand_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.ImmOperand'. */ -bool ensure_python_imm_operand_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.arch.ImmediateOperand'. */ +bool ensure_python_immediate_operand_is_registered(void); /* Tente de convertir en opérande de valeur immédiate. */ -int convert_to_imm_operand(PyObject *, void *); +int convert_to_immediate_operand(PyObject *, void *); diff --git a/plugins/pychrysalide/arch/operands/known.c b/plugins/pychrysalide/arch/operands/known.c index fab426e..85cabc2 100644 --- a/plugins/pychrysalide/arch/operands/known.c +++ b/plugins/pychrysalide/arch/operands/known.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.c - équivalent Python du fichier "arch/operands/known.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,70 +29,92 @@ #include <pygobject.h> -#include <arch/operands/known.h> +#include <arch/operands/known-int.h> #include "immediate.h" -#include "rename.h" #include "../../access.h" #include "../../helpers.h" -/* Crée un nouvel objet Python de type 'KnownImmOperand'. */ -static PyObject *py_known_imm_operand_new(PyTypeObject *, PyObject *, PyObject *); +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ +CREATE_DYN_CONSTRUCTOR(known_immediate_operand, G_TYPE_KNOWN_IMMEDIATE_OPERAND); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_known_immediate_operand_init(PyObject *, PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * -* Paramètres : type = type de l'objet à instancier. * +* Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * * * -* Description : Crée un nouvel objet Python de type 'KnownImmOperand'. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Instance Python mise en place. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_known_imm_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_known_immediate_operand_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *result; /* Instance à retourner */ - GImmOperand *imm; /* Opérande à remplacer */ + GImmediateOperand *imm; /* Opérande à remplacer */ const char *alt; /* Impression alternative */ int ret; /* Bilan de lecture des args. */ - GArchOperand *operand; /* Création GLib à transmettre */ - -#define KNOWN_IMM_OPERAND_DOC \ - "The KnownImmOperand provides replacement of" \ - " pychrysalide.arch.operands.ImmOperand instances by an alternative" \ - " text.\n" \ - "\n" \ - "Instances can be created using the following constructor:\n" \ - "\n" \ - " KnownImmOperand(imm, alt)" \ - "\n" \ - "Where imm is an operand of type pychrysalide.arch.operands.ImmOperand" \ - " and alt is a string providing the text to be rendered at object" \ + GKnownImmediateOperand *operand; /* Opérande natif à manipuler */ + +#define KNOWN_IMMEDIATE_OPERAND_DOC \ + "The KnownImmediateOperand provides replacement of" \ + " pychrysalide.arch.operands.ImmediateOperand instances by an alternative" \ + " text.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " KnownImmediateOperand(imm, alt)" \ + "\n" \ + "Where *imm* is an operand of type pychrysalide.arch.operands.ImmediateOperand" \ + " and *alt* is a string providing the text to be rendered at object" \ " display." - ret = PyArg_ParseTuple(args, "O&s", convert_to_imm_operand, &imm, &alt); - if (!ret) return NULL; + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "O&s", convert_to_immediate_operand, &imm, &alt); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ - operand = g_known_imm_operand_new(imm, alt); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - result = pygobject_new(G_OBJECT(operand)); + /* Eléments de base */ - g_object_unref(operand); + operand = G_KNOWN_IMMEDIATE_OPERAND(pygobject_get(self)); - return (PyObject *)result; + if (!g_known_immediate_operand_create(operand, imm, alt)) + return -1; + + return 0; } + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN IMMEDIAT CONNU */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : - * @@ -105,34 +127,36 @@ static PyObject *py_known_imm_operand_new(PyTypeObject *type, PyObject *args, Py * * ******************************************************************************/ -PyTypeObject *get_python_known_imm_operand_type(void) +PyTypeObject *get_python_known_immediate_operand_type(void) { - static PyMethodDef py_known_imm_operand_methods[] = { + static PyMethodDef py_known_immediate_operand_methods[] = { { NULL } }; - static PyGetSetDef py_known_imm_operand_getseters[] = { + static PyGetSetDef py_known_immediate_operand_getseters[] = { { NULL } }; - static PyTypeObject py_known_imm_operand_type = { + static PyTypeObject py_known_immediate_operand_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.arch.operands.KnownImmOperand", + .tp_name = "pychrysalide.arch.operands.KnownImmediateOperand", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = KNOWN_IMM_OPERAND_DOC, + .tp_doc = KNOWN_IMMEDIATE_OPERAND_DOC, - .tp_methods = py_known_imm_operand_methods, - .tp_getset = py_known_imm_operand_getseters, - .tp_new = py_known_imm_operand_new + .tp_methods = py_known_immediate_operand_methods, + .tp_getset = py_known_immediate_operand_getseters, + + .tp_init = py_known_immediate_operand_init, + .tp_new = py_known_immediate_operand_new, }; - return &py_known_imm_operand_type; + return &py_known_immediate_operand_type; } @@ -141,7 +165,7 @@ PyTypeObject *get_python_known_imm_operand_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.arch.KnownImmOperand'. * +* Description : Prend en charge l'objet '....KnownImmediateOperand'. * * * * Retour : Bilan de l'opération. * * * @@ -149,13 +173,13 @@ PyTypeObject *get_python_known_imm_operand_type(void) * * ******************************************************************************/ -bool ensure_python_known_imm_operand_is_registered(void) +bool ensure_python_known_immediate_operand_is_registered(void) { - PyTypeObject *type; /* Type Python 'ImmOperand' */ + PyTypeObject *type; /* Type 'KnownImmediateOperand'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - type = get_python_known_imm_operand_type(); + type = get_python_known_immediate_operand_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -163,13 +187,10 @@ bool ensure_python_known_imm_operand_is_registered(void) dict = PyModule_GetDict(module); - if (!ensure_python_imm_operand_is_registered()) - return false; - - if (!ensure_python_renamed_operand_is_registered()) + if (!ensure_python_immediate_operand_is_registered()) return false; - if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_IMM_OPERAND, type)) + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_IMMEDIATE_OPERAND, type)) return false; } @@ -192,11 +213,11 @@ bool ensure_python_known_imm_operand_is_registered(void) * * ******************************************************************************/ -int convert_to_known_imm_operand(PyObject *arg, void *dst) +int convert_to_known_immediate_operand(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_known_imm_operand_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_known_immediate_operand_type()); switch (result) { @@ -210,7 +231,7 @@ int convert_to_known_imm_operand(PyObject *arg, void *dst) break; case 1: - *((GKnownImmOperand **)dst) = G_KNOWN_IMM_OPERAND(pygobject_get(arg)); + *((GKnownImmediateOperand **)dst) = G_KNOWN_IMMEDIATE_OPERAND(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/arch/operands/known.h b/plugins/pychrysalide/arch/operands/known.h index b5ced68..f5b80e8 100644 --- a/plugins/pychrysalide/arch/operands/known.h +++ b/plugins/pychrysalide/arch/operands/known.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * known.h - prototypes pour l'équivalent Python du fichier "arch/operands/known.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,13 +32,13 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_known_imm_operand_type(void); +PyTypeObject *get_python_known_immediate_operand_type(void); -/* Prend en charge l'objet 'pychrysalide.arch.KnownImmOperand'. */ -bool ensure_python_known_imm_operand_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.arch.KnownImmediateOperand'. */ +bool ensure_python_known_immediate_operand_is_registered(void); /* Tente de convertir en remplaçant d'opérande d'immédiat. */ -int convert_to_known_imm_operand(PyObject *, void *); +int convert_to_known_immediate_operand(PyObject *, void *); diff --git a/plugins/pychrysalide/arch/operands/module.c b/plugins/pychrysalide/arch/operands/module.c index 89adecc..486e259 100644 --- a/plugins/pychrysalide/arch/operands/module.c +++ b/plugins/pychrysalide/arch/operands/module.c @@ -28,14 +28,20 @@ #include <assert.h> +/* #include "feeder.h" +*/ #include "immediate.h" #include "known.h" +/* #include "proxy.h" +*/ #include "register.h" +/* #include "rename.h" #include "target.h" #include "targetable.h" +*/ #include "../../helpers.h" @@ -101,15 +107,21 @@ bool populate_arch_operands_module(void) result = true; + /* if (result) result = ensure_python_proxy_feeder_is_registered(); - if (result) result = ensure_python_imm_operand_is_registered(); - if (result) result = ensure_python_known_imm_operand_is_registered(); + */ + if (result) result = ensure_python_immediate_operand_is_registered(); + if (result) result = ensure_python_known_immediate_operand_is_registered(); + /* if (result) result = ensure_python_proxy_operand_is_registered(); + */ if (result) result = ensure_python_register_operand_is_registered(); + /* if (result) result = ensure_python_renamed_operand_is_registered(); if (result) result = ensure_python_renameable_operand_is_registered(); if (result) result = ensure_python_target_operand_is_registered(); if (result) result = ensure_python_targetable_operand_is_registered(); + */ assert(result); diff --git a/plugins/pychrysalide/arch/operands/register.c b/plugins/pychrysalide/arch/operands/register.c index 2a48a0f..707524a 100644 --- a/plugins/pychrysalide/arch/operands/register.c +++ b/plugins/pychrysalide/arch/operands/register.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register.c - équivalent Python du fichier "arch/operands/register.c" * - * Copyright (C) 2019-2020 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -30,25 +30,19 @@ #include <i18n.h> #include <arch/operands/register-int.h> -#include <plugins/dt.h> #include "../operand.h" #include "../register.h" #include "../../access.h" #include "../../helpers.h" -#include "../../glibext/bufferline.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_register_operand_new(PyTypeObject *, PyObject *, PyObject *); - -/* Initialise la classe des descriptions de fichier binaire. */ -static void py_register_operand_init_gclass(GRegisterOperandClass *, gpointer); +CREATE_DYN_CONSTRUCTOR(register_operand, G_TYPE_REGISTER_OPERAND); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_register_operand_init(PyObject *, PyObject *, PyObject *); @@ -58,12 +52,6 @@ static int py_register_operand_init(PyObject *, PyObject *, PyObject *); /* ------------------------- REGISTRE SOUS FORME D'OPERANDE ------------------------- */ -/* Compare un opérande avec un autre. */ -static PyObject *py_register_operand___cmp__(PyObject *, PyObject *); - -/* Traduit un opérande en version humainement lisible. */ -static PyObject *py_register_operand__print(PyObject *, PyObject *); - /* Fournit le registre associé à l'opérande. */ static PyObject *py_register_operand_get_register(PyObject *, void *); @@ -76,86 +64,6 @@ static PyObject *py_register_operand_get_register(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - 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 */ - - /* Validations diverses */ - - base = get_python_register_operand_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_REGISTER_OPERAND, type->tp_name, - (GClassInitFunc)py_register_operand_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * -* * -* Description : Initialise la classe des descriptions de fichier binaire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void py_register_operand_init_gclass(GRegisterOperandClass *class, gpointer unused) -{ - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -182,7 +90,7 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw "\n" \ " RegisterOperand(reg)" \ "\n" \ - "Where reg is an architecture register defined from a subclass of" \ + "Where *reg* is an architecture register defined from a subclass of" \ " pychrysalide.arch.ArchRegister." /* Récupération des paramètres */ @@ -199,8 +107,8 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw operand = G_REGISTER_OPERAND(pygobject_get(self)); - g_object_ref(G_OBJECT(reg)); - operand->reg = reg; + if (!g_register_operand_create(operand, reg)) + return -1; return 0; @@ -215,98 +123,6 @@ static int py_register_operand_init(PyObject *self, PyObject *args, PyObject *kw /****************************************************************************** * * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Compare un opérande avec un autre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand___cmp__(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GRegisterOperand *other; /* Autre opérande à manipuler */ - int ret; /* Bilan de lecture des args. */ - GRegisterOperand *operand; /* Elément à manipuler */ - int status; /* Bilan de comparaison */ - -#define REGISTER_OPERAND_CMP_METHOD PYTHON_METHOD_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, py_register_operand, \ - "Implementation of the required method used to compare the" \ - " operand with another one. This second object is always" \ - " a pychrysalide.arch.RegisterOperand instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_register_operand, &other); - if (!ret) return NULL; - - operand = G_REGISTER_OPERAND(pygobject_get(self)); - - status = g_arch_operand_compare(G_ARCH_OPERAND(operand), G_ARCH_OPERAND(other)); - - result = PyLong_FromLong(status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = serveur à manipuler. * -* args = arguments associés à l'appel. * -* * -* Description : Traduit un opérande en version humainement lisible. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_register_operand__print(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GBufferLine *line; /* Ligne fournie à peupler */ - int ret; /* Bilan de lecture des args. */ - GRegisterOperand *operand; /* Elément à manipuler */ - -#define REGISTER_OPERAND_PRINT_METHOD PYTHON_METHOD_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, py_register_operand, \ - "Implementation of the required method used to print the operand" \ - " into a rendering line, which is a provided" \ - " pychrysalide.glibext.BufferLine instance.\n" \ - "\n" \ - "See the parent class for more information about this method." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_buffer_line, &line); - if (!ret) return NULL; - - operand = G_REGISTER_OPERAND(pygobject_get(self)); - - g_arch_operand_print(G_ARCH_OPERAND(operand), line); - - result = Py_None; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -367,8 +183,6 @@ static PyObject *py_register_operand_get_register(PyObject *self, void *closure) PyTypeObject *get_python_register_operand_type(void) { static PyMethodDef py_register_operand_methods[] = { - REGISTER_OPERAND_CMP_METHOD, - REGISTER_OPERAND_PRINT_METHOD, { NULL } }; diff --git a/plugins/pychrysalide/arch/register.c b/plugins/pychrysalide/arch/register.c index 615a5b7..7139e47 100644 --- a/plugins/pychrysalide/arch/register.c +++ b/plugins/pychrysalide/arch/register.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * register.c - équivalent Python du fichier "arch/register.c" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,32 +31,27 @@ #include <i18n.h> #include <arch/register-int.h> -#include <plugins/dt.h> #include "../access.h" #include "../helpers.h" -#include "../analysis/storage/serialize.h" +#include "../glibext/comparable.h" +#include "../glibext/hashable.h" +#include "../glibext/serialize.h" +#include "../glibext/strbuilder.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_arch_register_new(PyTypeObject *, PyObject *, PyObject *); - /* Initialise la classe des registres. */ -static void py_arch_register_init_gclass(GArchRegisterClass *, gpointer); - -/* Produit une empreinte à partir d'un registre. */ -static guint py_arch_register___hash___wrapper(const GArchRegister *); +static int py_arch_register_init_gclass(GArchRegisterClass *, PyTypeObject *); -/* Compare un registre avec un autre. */ -static int py_arch_register___cmp___wrapper(const GArchRegister *, const GArchRegister *); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(arch_register, G_TYPE_ARCH_REGISTER); -/* Traduit un registre en version humainement lisible. */ -static void py_arch_register_print_wrapper(const GArchRegister *, GBufferLine *); +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_arch_register_init(PyObject *, PyObject *, PyObject *); /* Indique si le registre correspond à ebp ou similaire. */ static bool py_arch_register_is_base_pointer_wrapper(const GArchRegister *); @@ -69,9 +64,6 @@ static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *); /* ---------------------------- PUR REGISTRE DU MATERIEL ---------------------------- */ -/* Effectue une comparaison avec un objet Python 'ArchRegister'. */ -static PyObject *py_arch_register_richcompare(PyObject *, PyObject *, int); - /* Indique si le registre correspond à ebp ou similaire. */ static PyObject *py_arch_register_is_base_pointer(PyObject *, void *); @@ -87,88 +79,8 @@ static PyObject *py_arch_register_is_stack_pointer(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de registre */ - bool status; /* Bilan d'un enregistrement */ - -#define ARCH_REGISTER_DOC \ - "The ArchRegister object aims to get subclassed to create" \ - " registers suitable for new architectures.\n" \ - "\n" \ - "Calls to the *__init__* constructor of this abstract object expect"\ - " no particular argument.\n" \ - "\n" \ - "The following methods have to be defined for new classes:\n" \ - "* pychrysalide.arch.ArchRegister.__hash__();\n" \ - "* pychrysalide.arch.ArchRegister.__cmp__();\n" \ - "* pychrysalide.arch.ArchRegister._print();\n" \ - "* pychrysalide.arch.ArchRegister._is_base_pointer();\n" \ - "* pychrysalide.arch.ArchRegister._is_stack_pointer().\n" \ - "\n" \ - "Chrysalide creates an internal glue to provide rich comparisons" \ - " for registers based on the old-style *__cmp__* function." - - /* Validations diverses */ - - base = get_python_arch_register_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é */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_ARCH_REGISTER, type->tp_name, - (GClassInitFunc)py_arch_register_init_gclass, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - 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 : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des registres. * * * @@ -178,190 +90,51 @@ static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObje * * ******************************************************************************/ -static void py_arch_register_init_gclass(GArchRegisterClass *class, gpointer unused) -{ - class->hash = py_arch_register___hash___wrapper; - class->compare = py_arch_register___cmp___wrapper; - class->print = py_arch_register_print_wrapper; - class->is_bp = py_arch_register_is_base_pointer_wrapper; - class->is_sp = py_arch_register_is_stack_pointer_wrapper; - -} - - -/****************************************************************************** -* * -* Paramètres : reg = registre visé par la procédure. * -* * -* Description : Produit une empreinte à partir d'un registre. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static guint py_arch_register___hash___wrapper(const GArchRegister *reg) -{ - guint result; /* Empreinte à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object. The" \ - " result must be an integer value." \ -) - - result = 0; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(reg)); - - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); - - Py_DECREF(pyret); - - } - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier registre à consulter. * -* b = second registre à consulter. * -* * -* Description : Compare un registre avec un autre. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int py_arch_register___cmp___wrapper(const GArchRegister *a, const GArchRegister *b) +static int py_arch_register_init_gclass(GArchRegisterClass *gclass, PyTypeObject *pyclass) { - int result; /* Empreinte à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_CMP_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __cmp__, "$self, other, /", \ - METH_VARARGS, \ - "Abstract method used to compare the register with another" \ - " one. This second object is always an" \ - " pychrysalide.arch.ArchRegister instance.\n" \ - "\n" \ - " This is the Python old-style comparison method, but" \ - " Chrysalide provides a glue to automatically build a rich" \ - " version of this function." \ -) - - result = 0; - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(a)); - - if (has_python_method(pyobj, "__cmp__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(b))); - - pyret = run_python_method(pyobj, "__cmp__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsLong(pyret); - } + PY_CLASS_SET_WRAPPER(gclass->is_bp, py_arch_register_is_base_pointer_wrapper); + PY_CLASS_SET_WRAPPER(gclass->is_sp, py_arch_register_is_stack_pointer_wrapper); - Py_DECREF(args); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - - return result; + return 0; } /****************************************************************************** * * -* Paramètres : reg = registre visé par la procédure. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Traduit un registre en version humainement lisible. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : - * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static void py_arch_register_print_wrapper(const GArchRegister *reg, GBufferLine *line) +static int py_arch_register_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ - PyObject *pyret; /* Bilan de consultation */ - -#define ARCH_REGISTER_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _print, "$self, line, /", \ - METH_VARARGS, \ - "Abstract method used to print the register into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(reg)); - - if (has_python_method(pyobj, "_print")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - - pyret = run_python_method(pyobj, "_print", args); - - Py_DECREF(args); + int ret; /* Bilan de lecture des args. */ - Py_XDECREF(pyret); +#define ARCH_REGISTER_DOC \ + "The ArchRegister object aims to get subclassed in order to create" \ + " registers suitable for new architectures.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect"\ + " no particular argument.\n" \ + "\n" \ + "The following methods may to be implemnted for new classes:\n" \ + "* pychrysalide.arch.ArchRegister._is_base_pointer();\n" \ + "* pychrysalide.arch.ArchRegister._is_stack_pointer().\n" - } + /* Initialisation d'un objet GLib */ - Py_DECREF(pyobj); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - PyGILState_Release(gstate); + return 0; } @@ -486,51 +259,6 @@ static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *reg) /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet Python 'ArchRegister'.* -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_arch_register_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - const GArchRegister *reg_a; /* Premier élément à traiter */ - const GArchRegister *reg_b; /* Second élément à traiter */ - int status; /* Résultat d'une comparaison */ - - ret = PyObject_IsInstance(b, (PyObject *)get_python_arch_register_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - reg_a = G_ARCH_REGISTER(pygobject_get(a)); - reg_b = G_ARCH_REGISTER(pygobject_get(b)); - - status = py_arch_register___cmp___wrapper(reg_a, reg_b); - - result = status_to_rich_cmp_state(status, op); - - cmp_done: - - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -618,9 +346,6 @@ static PyObject *py_arch_register_is_stack_pointer(PyObject *self, void *closure PyTypeObject *get_python_arch_register_type(void) { static PyMethodDef py_arch_register_methods[] = { - ARCH_REGISTER_HASH_WRAPPER, - ARCH_REGISTER_CMP_WRAPPER, - ARCH_REGISTER_PRINT_WRAPPER, ARCH_REGISTER_IS_BASE_POINTER_WRAPPER, ARCH_REGISTER_IS_STACK_POINTER_WRAPPER, { NULL } @@ -643,11 +368,10 @@ PyTypeObject *get_python_arch_register_type(void) .tp_doc = ARCH_REGISTER_DOC, - .tp_richcompare = py_arch_register_richcompare, - .tp_methods = py_arch_register_methods, .tp_getset = py_arch_register_getseters, + .tp_init = py_arch_register_init, .tp_new = py_arch_register_new, }; @@ -683,9 +407,20 @@ bool ensure_python_arch_register_is_registered(void) dict = PyModule_GetDict(module); + if (!ensure_python_comparable_object_is_registered()) + return false; + + if (!ensure_python_hashable_object_is_registered()) + return false; + if (!ensure_python_serializable_object_is_registered()) return false; + if (!ensure_python_string_builder_is_registered()) + return false; + + pyg_register_class_init(G_TYPE_ARCH_REGISTER, (PyGClassInitFunc)py_arch_register_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_ARCH_REGISTER, type)) return false; diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c new file mode 100644 index 0000000..f120c3b --- /dev/null +++ b/plugins/pychrysalide/bindings.c @@ -0,0 +1,1523 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.c - éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "bindings.h" + + +#include <assert.h> +#include <dlfcn.h> +#include <pygobject.h> +#include <stddef.h> +#include <stdio.h> + + +#include <common/cpp.h> +#include <common/environment.h> +#include <common/extstr.h> +#include <core/core.h> +#include <plugins/pglist.h> +#include <plugins/self.h> + + +#include "access.h" +#include "constants.h" +#include "helpers.h" +#include "star.h" +//#include "strenum.h" +#include "struct.h" +#include "analysis/module.h" +#include "arch/module.h" +#include "common/module.h" +#include "core/module.h" +#include "glibext/module.h" +/* #include "debug/module.h" */ +#include "format/module.h" +/* #include "mangling/module.h" */ +#include "plugins/module.h" + + + + + +/* ------------------------ FONCTIONNALITES POUR CODE PYTHON ------------------------ */ + + +#define PYCHRYSALIDE_NAME "pychrysalide" + +#define PYCHRYSALIDE_DOC \ + "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ + "\n" \ + "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ + "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ + "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ + "\n" \ + "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ + "how the API works.\n" \ + "\n" \ + "These plugins are located in the 'plugins/python' directory.\n" \ + "\n" \ + "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." + + +/* Fournit la révision du programme global. */ +static PyObject *py_chrysalide_revision(PyObject *, PyObject *); + +/* Fournit la version du programme global. */ +static PyObject *py_chrysalide_version(PyObject *, PyObject *); + +/* Fournit la version du greffon pour Python. */ +static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); + + + +/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */ + + +/* Détermine si l'interpréteur lancé est celui pris en compte. */ +static bool is_current_abi_suitable(void); + +/* Assure une pleine initialisation des objets de Python-GI. */ +static bool install_metaclass_for_python_gobjects(void); + +/* Met en place un environnement pour l'extension Python. */ +static bool setup_python_context(void); + +/* Intègre les éventuelles fonctions natives des interfaces. */ +static void inherit_interface_slots(PyObject *); + +/** + * Conservation d'anciens pointeurs remplacés + */ +static initproc __old_gobject_meta_base_init = NULL; +static initproc __old_gobject_meta_init = NULL; + +/** + * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction + * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule() + * et PyObject_GetAttrString(). + * + * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction + * PyExit_pychrysalide() provoque l'erreur suivante : + * + * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) + * + * Les accès nécessaires sont donc conservés ici. + */ +static PyTypeObject *__gobject_meta_base = NULL; +static PyTypeObject *__gobject_meta = NULL; + +/* Interceptionne une initialisation pour types gi._gi.GObject. */ +static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *); + +/* Interceptionne une initialisation pour types GObject.Object. */ +static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *); + +/* Modifie légèrement le comportement des GObjects en Python. */ +static bool hook_pygobject_behaviour(void); + +/* Restaure le comportement d'origine des GObjects en Python. */ +static void unhook_pygobject_behaviour(void); + +/* Assure la définition d'un type GObject pour Python adapté. */ +static void ensure_native_pygobject_type(PyTypeObject **); + +/* Fournit la référence à un éventuel module déjà en place. */ +static PyObject *get_existing_modules(void); + +/* Définit les différents modules du support Python. */ +static PyObject *create_basic_modules(const pyinit_details_t *); + +/* Inscrit les défintions des objets Python de Chrysalide. */ +static bool populate_python_modules(const pyinit_details_t *); + +/* Restore une ancienne définition de type GObject au besoin. */ +static void restore_original_pygobject_type(PyTypeObject *); + + + +/* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */ + + +/* Assure le plein chargement dans un interpréteur Python. */ +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *); + +/* Point de sortie pour l'initialisation de Python. */ +static void PyExit_pychrysalide(void); + + + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES POUR CODE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la révision du programme global. * +* * +* Retour : Numéro de révision. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + +#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ +( \ + revision, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the revision number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: 'r1665'." \ +) + + result = PyUnicode_FromString("r" XSTR(REVISION)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du programme global. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + int major; /* Numéro de version majeur */ + int minor; /* Numéro de version mineur */ + int revision; /* Numéro de révision */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '1.6.65'." \ +) + + major = REVISION / 1000; + minor = (REVISION - (major * 1000)) / 100; + revision = REVISION % 100; + + snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); + + result = PyUnicode_FromString(version); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du greffon pour Python. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + mod_version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide module for Python.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '0.1.0'." \ +) + + snprintf(version, sizeof(version), "%s", "x.x.x");// FIXME _chrysalide_plugin.version); + + result = PyUnicode_FromString(version); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE MISE EN PLACE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* * +* Retour : true si l'exécution peut se poursuivre, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_current_abi_suitable(void) +{ + bool result; + int fds[2]; + int ret; + char cmds[128]; + char content[64]; + ssize_t got; + +#define GRAB_ABI_FLAGS_IN_PYTHON \ + "import sys" "\n" \ + "import os" "\n" \ + "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ + "os.write(%d, data)" "\n" + + result = false; + + ret = pipe(fds); + if (ret == -1) + { + perror("pipe()"); + goto exit; + } + + snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); + + ret = PyRun_SimpleString(cmds); + if (ret != 0) goto exit_with_pipe; + + got = read(fds[0], content, sizeof(content)); + if (got < 0) + { + perror("read()"); + goto exit_with_pipe; + } + + content[got] = '\0'; + + result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); + + exit_with_pipe: + + close(fds[0]); + close(fds[1]); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ + "the ones of the Python library used during the module compilation."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure une pleine initialisation des objets de Python-GI. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool install_metaclass_for_python_gobjects(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + /** + * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), + * qui fait appel à create_python_plugin(). Une instance y est construite via un + * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad + * dans le fichier __init__.py présent dans chaque module d'extension. + * + * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique + * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction + * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. + * Le code de cette dernière comprend notamment la portion suivante : + * + * [...] + * Py_SET_TYPE(type, PyGObject_MetaType); + * [...] + * if (PyType_Ready(type) < 0) { + * g_warning ("couldn't make the type `%s' ready", type->tp_name); + * return; + * } + * [...] + * + * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c + * et commence par : + * + * int PyType_Ready(PyTypeObject *type) + * { + * if (type->tp_flags & Py_TPFLAGS_READY) { + * assert(_PyType_CheckConsistency(type)); + * return 0; + * } + * [...] + * } + * + * La vérification de cohérencce commence par analyser le type et son propre + * type : + * + * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : + * + * int _PyType_CheckConsistency(PyTypeObject *type) + * { + * [...] + * CHECK(!_PyObject_IsFreed((PyObject *)type)); + * [...] + * } + * + * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : + * + * int _PyObject_IsFreed(PyObject *op) + * { + * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { + * return 1; + * } + * + * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. + * + * Or le type du type est écrasé dans la fonction pygobject_register_class() + * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est + * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : + * + * static PyObject * + * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) + * { + * Py_INCREF(metaclass); + * PyGObject_MetaType = metaclass; + * Py_INCREF(metaclass); + * + * Py_SET_TYPE(&PyGObject_Type, metaclass); + * + * Py_INCREF(Py_None); + * return Py_None; + * } + * + * Afin de valider la vérification de _PyType_CheckConsistency() pour les + * modules externes qui entraînent un enregistrement tout en portant le drapeau + * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut + * initialiser au besoin la variable PyGObject_MetaType. + * + * Une ligne suffit donc à enregistrer le type intermédiaire : + * + * from _gi import types + * + * On simule ici une déclaration similaire si nécessaire, selon la valeur + * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name : + * - "type" (PyType_Type) : état initial ; + * - "_GObjectMetaBase" : état revu. + */ + + /** + * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ? + */ + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + if (!result) + { + gi_types_mod = PyImport_ImportModule("gi.types"); + + result = (PyErr_Occurred() == NULL); + + if (result) + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + Py_XDECREF(gi_types_mod); + + } + +#ifndef NDEBUG + if (result) + assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0); +#endif + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Met en place un environnement pour l'extension Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_python_context(void) +{ + bool result; /* Bilan à retourner */ + + result = false; + + /** + * Un message d'erreur doit être défini en cas d'échec de l'initialisation, + * via un appel à PyErr_SetString(). + */ + + if (!is_current_abi_suitable()) + goto exit; + + if (pygobject_init(-1, -1, -1) == NULL) + { + PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); + goto exit; + } + + if (!install_metaclass_for_python_gobjects()) + goto exit; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : cls = classe instanciée pour la construction d'un objet. * +* * +* Description : Intègre les éventuelles fonctions natives des interfaces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void inherit_interface_slots(PyObject *cls) +{ + GType gtype; /* Type GObject lié au Python */ + GType *ifaces; /* Interfaces implémentées */ + guint ifaces_count; /* Nombre de ces interfaces */ + guint i; /* Boucle de parcours */ + PyTypeObject *iface_type; /* Type Python pour interface */ + size_t k; /* Boucle de parcours */ + size_t offset; /* Position dans une structure */ + void *src_slot; /* Eventuelle fonction idéale */ + void *dst_slot; /* Eventuelle fonction en place*/ + + static size_t slot_offsets[] = { /* Emplacements à actualiser */ + //offsetof(PyTypeObject, tp_str), + offsetof(PyTypeObject, tp_hash), + offsetof(PyTypeObject, tp_richcompare), + }; + + /** + * Cette fonction reprend les principes de la fonction d'importation de + * PyGObject pygobject_inherit_slots(). + * + * Cependant, cette dernière n'est appelée que depuis les fonctions : + * - pygobject_register_class() (send C -> Python), qui peut écraser des + * slots existants ; + * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne + * remplace pas les fonctions par défaut déjà en place. + * + * Pour mémoire, les types créés dynamiquement depuis des scripts (sens + * Python -> C) passent par la fonction _wrap_pyg_type_register(). + */ + + gtype = pyg_type_from_object(cls); + assert(gtype != G_TYPE_INVALID); + + ifaces = g_type_interfaces(gtype, &ifaces_count); + + for (i = 0; i < ifaces_count; i++) + { + iface_type = pygobject_lookup_class(ifaces[i]); + +#define PYTYPE_SLOT(tp, off) \ + *(void **)(void *)(((char *)tp) + off) + + for (k = 0; k < ARRAY_SIZE(slot_offsets); k++) + { + offset = slot_offsets[k]; + + src_slot = PYTYPE_SLOT(iface_type, offset); + + if (src_slot == NULL) + continue; + + if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset) + || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + + dst_slot = PYTYPE_SLOT(cls, offset); + + if (src_slot == dst_slot) + continue; + + if (dst_slot != NULL) + { + if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset) + && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + } + + /** + * Usage du *(void **)(void *) + */ + PYTYPE_SLOT(cls, offset) = src_slot; + + } + + } + + g_free(ifaces); + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types gi._gi.GObject. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase. + */ + + result = __old_gobject_meta_base_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types GObject.Object. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici GObjectMeta. + */ + + result = __old_gobject_meta_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Modifie légèrement le comportement des GObjects en Python. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool hook_pygobject_behaviour(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + result = false; + + /** + * Validation des accès. + * + * Les références prises sur les attributs sont restituées dans + * unhook_pygobject_behaviour(). + */ + + gi_types_mod = PyImport_ImportModule("gi.types"); + if (gi_types_mod == NULL) goto exit; + + __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase"); + assert(__gobject_meta_base != NULL); + if (__gobject_meta_base == NULL) goto exit_with_mod; + + __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta"); + assert(__gobject_meta != NULL); + if (__gobject_meta == NULL) goto exit_with_mod; + + /** + * Modification des comportements. + */ + + __old_gobject_meta_base_init = __gobject_meta_base->tp_init; + + __gobject_meta_base->tp_init = hook_gobject_meta_base_init; + + __old_gobject_meta_init = __gobject_meta->tp_init; + + __gobject_meta->tp_init = hook_gobject_meta_init; + + result = true; + + exit_with_mod: + + Py_DECREF(gi_types_mod); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Restaure le comportement d'origine des GObjects en Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unhook_pygobject_behaviour(void) +{ + /** + * Le déclenchement de la fonction PyExit_pychrysalide() appelante est + * programmé depuis init_python_pychrysalide_module(), appelée si + * hook_pygobject_behaviour() a opéré avec réussite. + */ + assert(__gobject_meta_base != NULL); + assert(__gobject_meta != NULL); + + __gobject_meta_base->tp_init = __old_gobject_meta_base_init; + + Py_XDECREF(__gobject_meta_base); + + __gobject_meta->tp_init = __old_gobject_meta_init; + + Py_XDECREF(__gobject_meta); + +} + + +/****************************************************************************** +* * +* Paramètres : namespace = module particulier à charger à partir de gi. * +* version = idenfiant de la version à stipuler. * +* * +* Description : Charge un module GI dans Python avec une version attendue. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool import_namespace_from_gi_repository(const char *namespace, const char *version) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module Python-GObject */ + PyObject *args; /* Arguments à fournir */ + int ret; /* Bilan d'une mise en place */ + + result = false; + + /* Sélection d'une version */ + + module = PyImport_ImportModule("gi"); + + if (module != NULL) + { + args = Py_BuildValue("ss", namespace, version); + + run_python_method(module, "require_version", args); + + result = (PyErr_Occurred() == NULL); + + Py_DECREF(args); + Py_DECREF(module); + + } + + /* Importation du module visé */ + + if (result) + { + args = PyTuple_New(1); + + ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); + if (ret != 0) + { + result = false; + goto args_error; + } + + module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); + + result = (module != NULL); + + Py_XDECREF(module); + + args_error: + + Py_DECREF(args); + + } + + // TODO : message d'erreur ? + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Assure la définition d'un type GObject pour Python adapté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ensure_native_pygobject_type(PyTypeObject **py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + /** + * Les appels suivants procèdent à l'enregistrement de différents éléments + * dans l'espace de noms Python pour Chrysalide. + * + * Une majeure partie de ces éléments est constituée d'objets dérivés de + * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types + * en Python : + * + * - gi._gi.GObject, mis en place lors de l'importation du module gi. + * + * Ce dernier lance automatiquement l'importation du module natif gi._gi, + * lequel, via son initialisation dans la fonction PyInit__gi() + * (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types() + * qui procède à l'enregistrement du type "GObject" porté par la structure + * PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel + * à pygobject_register_class() dans gi/pygobject-object.c). + * + * - gi.repository.GObject.Object, qui vient surclasser le type précédent + * lors d'un appel d'initialisation : from gi.repository import GObject. + * + * Cette seconde définition est destinée à apporter une représentation + * de l'introspection GObject de plus haut niveau pour l'utilisateur par + * rapport à celle de bas niveau gi._gi. + * + * Il demeure que la seconde définition est entièrement implémentée en Python + * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière + * propriétée à tous les objets qui en dérivent. + * + * Les définitions de Chrysalide sont cependant toutes statiques et donc + * incompatibles avec une définition gi.repository.GObject.Object, comme le + * pointent les validations opérées par PyType_Ready(). + * + * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject + * brute, effectuer les enregistrements de Chrysalide sur cette base, et + * remettre en place la définition éventuellement remplacée ensuite. + */ + + *py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT); + + if (*py_gobj_def != &PyGObject_Type) + { + Py_INCREF((PyObject *)*py_gobj_def); + + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit la référence à un éventuel module déjà en place. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *get_existing_modules(void) +{ + PyObject *result; /* Module Python à retourner */ + + /** + * Vérification préalable : dans le cas où on est embarqué directement dans + * un interpréteur Python, le module se charge et termine par charger à leur + * tour les différentes extensions trouvées, via load_remaning_plugins() puis + * chrysalide_plugin_on_native_loaded(). + * + * Lesquelles vont très probablement charger le module pychrysalide. + * + * Comme le chargement de ce dernier n'est alors pas encore terminé, + * Python va relancer cette procédure, et register_access_to_python_module() + * va détecter un doublon. + */ + + result = get_access_to_python_module(PYCHRYSALIDE_NAME); + + Py_XINCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Définit les différents modules du support Python. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *create_basic_modules(const pyinit_details_t *details) +{ + PyObject *result; /* Module Python à retourner */ + bool status; /* Bilan des inclusions */ + + static PyMethodDef py_chrysalide_methods[] = { + PY_CHRYSALIDE_REVISION_METHOD, + PY_CHRYSALIDE_VERSION_METHOD, + PY_CHRYSALIDE_MOD_VERSION_METHOD, + { NULL } + }; + + static PyModuleDef py_chrysalide_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = PYCHRYSALIDE_NAME, + .m_doc = PYCHRYSALIDE_DOC, + + .m_size = -1, + + .m_methods = py_chrysalide_methods + + }; + + result = PyModule_Create(&py_chrysalide_module); + + register_access_to_python_module(py_chrysalide_module.m_name, result); + + status = true; + + /** + * Réceptacle pour objets et constantes : à laisser en premier ajout donc. + */ + if (status) status = add_features_module(result); + + if (status) status = define_data_types_constants(result); + + if (status) status = add_analysis_module(result); + if (status) status = add_arch_module(result); + if (status) status = add_common_module(result); + if (status) status = add_glibext_module(result); + if (status) status = add_core_module(result); + /* + if (status) status = add_debug_module(result); + */ + if (status) status = add_format_module(result); + /* + if (status) status = add_mangling_module(result); + */ + if (status) status = add_plugins_module(result); + + /** + * Ajout de modules UI supplémentaires éventuels. + */ + + if (status && details->add_extra != NULL) + status = details->add_extra(result); + + if (!status) + { + Py_DECREF(result); + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Inscrit les défintions des objets Python de Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_python_modules(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + + /** + * Les chargements de types supplémentaires, apportés par la version UI, ne + * peuvent être forcés depuis les mises en places des versions non-UI via + * les classiques appels ensure_xxx(). + * + * L'assurance d'un chargement préalable est ainsi réalisée ici, via + * un chargement préliminaire, si besoin est. + */ + + if (details->populate_extra != NULL) + result = details->populate_extra(false); + else + result = true; + + /* + if (result) result = ensure_python_string_enum_is_registered(); + */ + if (result) result = ensure_python_py_struct_is_registered(); + + if (result) result = populate_analysis_module(); + if (result) result = populate_arch_module(); + if (result) result = populate_glibext_module(); + if (result) result = populate_common_module(); + if (result) result = populate_core_module(); + /* + if (result) result = populate_debug_module(); + */ + if (result) result = populate_format_module(); + /* + if (result) result = populate_mangling_module(); + */ + if (result) result = populate_plugins_module(); + + /** + * Certaines définitions reposent sur une déclinaison de GtkWidget, + * dont le chargement va remplacer la définition statique de GObject + * par une version allouée dynamiquement. + * + * De telles définitions doivent donc être prise en compte à la fin + * du chargement. + */ + + if (result && details->populate_extra != NULL) + result = details->populate_extra(true); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Restore une ancienne définition de type GObject au besoin. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void restore_original_pygobject_type(PyTypeObject *py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + if (py_gobj_def != &PyGObject_Type) + { + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def); + + Py_DECREF((PyObject *)py_gobj_def); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Implémente le point d'entrée pour l'initialisation de Python.* +* * +* Retour : Module mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *init_python_pychrysalide_module(const pyinit_details_t *details) +{ + PyObject *result; /* Module Python à retourner */ + PyTypeObject *py_gobj_def; /* Définition GObject courante */ + bool status; /* Bilan des inclusions */ + + result = get_existing_modules(); + + if (result != NULL) + return result; + + if (!setup_python_context()) + goto exit; + + if (!hook_pygobject_behaviour()) + goto exit; + + /** + * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, + * à savoir des types convertis de façon incomplète. Par exemple, pour une + * structure GChecksum, le type à l'exécution est : + * + * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] + * + * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] + * + * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec + * la version debug de Python3 (version de python3-gi : 3.42.2-3) : + * + * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() + * + * Code de reproduction dans un interpréteur classique : + * + * import gi + * gi.require_version('GLib', '2.0') + * from gi.repository import GLib + * + */ + + if (!import_namespace_from_gi_repository("GLib", "2.0")) + goto exit; + + /* Mise en place des fonctionnalités offertes */ + + ensure_native_pygobject_type(&py_gobj_def); + + result = create_basic_modules(details); + + if (result == NULL) + PyErr_SetString(PyExc_SystemError, "failed to create all PyChrysalide modules."); + + else + { + status = populate_python_modules(details); + + if (!status) + PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); + + else if (details->standalone) + status = init_python_interpreter_for_standalone_mode(details); + + if (!status) + { + Py_DECREF(result); + result = NULL; + } + + } + + restore_original_pygobject_type(py_gobj_def); + + exit: + + if (result == NULL && !details->standalone) + log_pychrysalide_exception("Python bindings loading failed"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* +* * +* Description : Présente dans le journal une exception survenue. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void log_pychrysalide_exception(const char *prefix, ...) +{ + va_list ap; /* Compléments argumentaires */ + char *msg; /* Message complet à imprimer */ + 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 */ + + assert(PyGILState_Check() == 1); + + if (PyErr_Occurred()) + { + /* Base de la communication */ + + va_start(ap, prefix); + + vasprintf(&msg, prefix, ap); + + va_end(ap); + + /* Détails complémentaires */ + + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + PyErr_NormalizeException(&err_type, &err_value, &err_traceback); + + if (err_traceback == NULL) + { + err_traceback = Py_None; + Py_INCREF(err_traceback); + } + + PyException_SetTraceback(err_value, err_traceback); + + if (err_value == NULL) + msg = stradd(msg, _(": no extra information is provided...")); + + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + msg = stradd(msg, ": "); + msg = stradd(msg, err_msg); + + Py_DECREF(err_string); + + } + + /** + * Bien que la documentation précise que la fonction PyErr_Fetch() + * transfère la propritété des éléments retournés, la pratique + * montre que le programme plante à la terminaison en cas d'exception. + * + * C'est par exemple le cas quand un greffon Python ne peut se lancer + * correctement ; l'exception est alors levée à partir de la fonction + * create_python_plugin() et le plantage intervient en sortie d'exécution, + * au moment de la libération de l'extension Python : + * + * ==14939== Jump to the invalid address stated on the next line + * ==14939== at 0x1A8FCBC9: ??? + * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) + * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) + * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) + * ==14939== by 0x10AD19: main (main.c:440) + * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd + * + * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un + * appel à PyErr_PrintEx(0) ne change rien. + * + * La seule différence de l'instruction set_sys_last_vars réside en quelques + * lignes dans le code de l'interpréteur Python : + * + * if (set_sys_last_vars) { + * _PySys_SetObjectId(&PyId_last_type, exception); + * _PySys_SetObjectId(&PyId_last_value, v); + * _PySys_SetObjectId(&PyId_last_traceback, tb); + * } + * + * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? + * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence + * à ces éléments. + * + * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). + */ + + PySys_SetObject("last_type", err_type); + PySys_SetObject("last_value", err_value); + PySys_SetObject("last_traceback", err_traceback); + + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + + log_plugin_simple_message(LMT_EXT_ERROR, msg); + + free(msg); + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = instance représentant le greffon courant. * +* path = chemin supplémentaire pour l'espace de recherche. * +* * +* Description : Complète les chemins de recherches de Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void extend_python_path(const GPluginModule *plugin, const char *path) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *list; /* Liste de chemins à compléter*/ + PyObject *new; /* Nouveau chemin à intégrer */ + + gstate = PyGILState_Ensure(); + + list = PySys_GetObject("path"); + assert(list != NULL); + + new = PyUnicode_FromString(path); + assert(new != NULL); + + PyList_Append(list, new); + + Py_DECREF(new); + + add_to_env_var("PYTHONPATH", path, ":"); + + PyGILState_Release(gstate); + + g_plugin_module_log_variadic_message(plugin, LMT_INFO, + _("PYTHONPATH environment variable set to '%s'"), + getenv("PYTHONPATH")); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONS GLOBALES DE CHRYSALIDE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Assure le plein chargement dans un interpréteur Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + int ret; /* Bilan de préparatifs */ + Dl_info info; /* Informations dynamiques */ + GModule *module; /* Structure de chargement GLib*/ + GPluginModule *self; /* Représentation interne */ + + result = false; + + ret = Py_AtExit(PyExit_pychrysalide); + if (ret == -1) + { + PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); + goto exit; + } + + if (!load_core_components(ACC_ALL_COMPONENTS)) + { + PyErr_SetString(PyExc_SystemError, "unable to load core components."); + goto exit; + } + + /** + * Le module chargé par Python n'apparaît pas dans la liste des greffons de + * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres + * extensions. + * + * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la + * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit + * déjà réservé pour faire échouer le second chargement du greffon courant + * lors du parcours des répertoires conservant les fichiers d'extensions. + */ + + ret = dladdr(__FUNCTION__, &info); + if (ret == 0) + { + LOG_ERROR_DL_N("dladdr"); + + PyErr_SetString(PyExc_SystemError, "failed to force bindings registration."); + goto exit; + + } + + module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY); + assert(module != NULL); + + self = details->create_self(module); + + /* A ce stade, le greffon a été chargé correctement */ + g_plugin_module_override_flags(self, PSF_LOADED); + + register_plugin(self); + + unref_object(self); + + /** + * Intégration des fonctionnalités portées par d'autres greffons. + */ + + result = true; + + init_all_plugins(true); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point de sortie pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void PyExit_pychrysalide(void) +{ + unhook_pygobject_behaviour(); + + exit_all_plugins(); + + unload_core_components(ACC_ALL_COMPONENTS); + +} diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h new file mode 100644 index 0000000..036f852 --- /dev/null +++ b/plugins/pychrysalide/bindings.h @@ -0,0 +1,86 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.h - prototypes pour les éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024-2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_BINDINGS_H +#define _PLUGINS_PYCHRYSALIDE_BINDINGS_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <gmodule.h> +#include <stdbool.h> + + +#include <plugins/plugin.h> + + + +/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */ + + +/* Charge un module GI dans Python avec une version attendue. */ +bool import_namespace_from_gi_repository(const char *, const char *); + +/* Raffinements pour la mise en place du module Python */ +typedef struct _pyinit_details_t +{ + bool standalone; /* Chargement depuis Python ? */ + + bool (* add_extra) (PyObject *); /* Ajout de modules ? */ + + bool (* populate_extra) (bool); /* Ajout de types ? */ + + /** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ + GPluginModule * (* create_self) (GModule *); + +} pyinit_details_t; + +/* Implémente le point d'entrée pour l'initialisation de Python. */ +PyObject *init_python_pychrysalide_module(const pyinit_details_t *); + +/* Présente dans le journal une exception survenue. */ +void log_pychrysalide_exception(const char *, ...); + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Complète les chemins de recherches de Python. */ +void extend_python_path(const GPluginModule *, const char *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */ diff --git a/plugins/pychrysalide/common/Makefile.am b/plugins/pychrysalide/common/Makefile.am index 43e1fc4..ad58900 100644 --- a/plugins/pychrysalide/common/Makefile.am +++ b/plugins/pychrysalide/common/Makefile.am @@ -6,7 +6,6 @@ noinst_LTLIBRARIES = libpychrysacommon.la # fnv1a.h fnv1a.c \ # hex.h hex.c \ # itoa.h itoa.c \ -# leb128.h leb128.c \ # module.h module.c \ # packed.h packed.c \ # pathname.h pathname.c \ @@ -15,6 +14,7 @@ noinst_LTLIBRARIES = libpychrysacommon.la libpychrysacommon_la_SOURCES = \ bits.h bits.c \ entropy.h entropy.c \ + leb128.h leb128.c \ module.h module.c \ xdg.h xdg.c diff --git a/plugins/pychrysalide/common/leb128.c b/plugins/pychrysalide/common/leb128.c index 8b15303..2eeb191 100644 --- a/plugins/pychrysalide/common/leb128.c +++ b/plugins/pychrysalide/common/leb128.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * leb128.c - équivalent Python du fichier "common/leb128.c" * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,13 +26,13 @@ #include <assert.h> +#include <malloc.h> #include <pygobject.h> #include <common/leb128.h> -#include "packed.h" #include "../access.h" #include "../helpers.h" @@ -69,31 +69,29 @@ static PyObject *py_leb128_pack_uleb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ uleb128_t value; /* Valeur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de l'opération */ - -#define LEB128_PACK_ULEB128_METHOD PYTHON_METHOD_DEF \ -( \ - pack_uleb128, "value, pbuf", \ - METH_VARARGS, py_leb128, \ - "Pack an unsigned LEB128 value into a data buffer.\n" \ - "\n" \ - "The *value* is an integer value. The *pbuf* argument has to" \ - " be a pychrysalide.common.PackedBuffer instance where data" \ - " will be appended.\n" \ - "\n" \ - "The returned value is the operation status: *True* for" \ - " success, *False* for failure." \ + size_t count; /* Nombre d'octets produits */ + void *bytes; /* Octets de représentation */ + +#define LEB128_PACK_ULEB128_METHOD PYTHON_METHOD_DEF \ +( \ + pack_uleb128, "value", \ + METH_VARARGS, py_leb128, \ + "Pack an unsigned LEB128 value into bytes.\n" \ + "\n" \ + "The *value* has to be an integer value.\n" \ + "\n" \ + "The returned value is byte data." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_uleb128_value, &value, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&", convert_to_uleb128_value, &value); if (!ret) return NULL; - status = pack_uleb128(&value, pbuf); + bytes = pack_uleb128(&value, &count); - result = status ? Py_True : Py_False; - Py_INCREF(result); + result = PyBytes_FromStringAndSize(bytes, count); + + free(bytes); return result; @@ -117,31 +115,29 @@ static PyObject *py_leb128_pack_leb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ leb128_t value; /* Valeur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de l'opération */ - -#define LEB128_PACK_LEB128_METHOD PYTHON_METHOD_DEF \ -( \ - pack_leb128, "value, pbuf", \ - METH_VARARGS, py_leb128, \ - "Pack a signed LEB128 value into a data buffer.\n" \ - "\n" \ - "The *value* is an integer value. The *pbuf* argument has to" \ - " be a pychrysalide.common.PackedBuffer instance where data" \ - " will be appended.\n" \ - "\n" \ - "The returned value is the operation status: *True* for" \ - " success, *False* for failure." \ + size_t count; /* Nombre d'octets produits */ + void *bytes; /* Octets de représentation */ + +#define LEB128_PACK_LEB128_METHOD PYTHON_METHOD_DEF \ +( \ + pack_leb128, "value", \ + METH_VARARGS, py_leb128, \ + "Pack a signed LEB128 value into bytes.\n" \ + "\n" \ + "The *value* has to be an integer value.\n" \ + "\n" \ + "The returned value is byte data." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_leb128_value, &value, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&", convert_to_leb128_value, &value); if (!ret) return NULL; - status = pack_leb128(&value, pbuf); + bytes = pack_leb128(&value, &count); + + result = PyBytes_FromStringAndSize(bytes, count); - result = status ? Py_True : Py_False; - Py_INCREF(result); + free(bytes); return result; @@ -164,33 +160,42 @@ static PyObject *py_leb128_pack_leb128(PyObject *self, PyObject *args) static PyObject *py_leb128_unpack_uleb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *bytes; /* Octets brutes transmis */ + Py_ssize_t count; /* Quantité de ces octets */ int ret; /* Bilan de lecture des args. */ + const void *pos; /* Tëte de lecture */ + const void *max; /* Position de lecture maximale*/ uleb128_t value; /* Valeur à manipuler */ bool status; /* Bilan de l'opération */ #define LEB128_UNPACK_ULEB128_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_uleb128, "pbuf", \ + unpack_uleb128, "buf", \ METH_VARARGS, py_leb128, \ - "Unpack an unsigned LEB128 value into a data buffer.\n" \ + "Unpack an unsigned LEB128 value from bytes.\n" \ "\n" \ - "The *pbuf* argument has to be a" \ - " pychrysalide.common.PackedBuffer instance from where data" \ - " will be read.\n" \ + "The *buf* argument needs to be bytes with enough data aimed" \ + " to get translated into an unsigned LEB128 value.\n" \ "\n" \ "The returned value depends on the operation status: *None*" \ - " for failure or a integer value for success." \ + " for failure or a tuple with two items for success: the" \ + " decoded value and the remaining bytes." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "y#", &bytes, &count); if (!ret) return NULL; - status = unpack_uleb128(&value, pbuf); + pos = bytes; + max = bytes + count; - if (status) - result = PyLong_FromUnsignedLongLong(value); + status = unpack_uleb128(&value, &pos, max); + if (status) + { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, PyLong_FromUnsignedLongLong(value)); + PyTuple_SetItem(result, 1, PyBytes_FromStringAndSize(pos, (char *)max - (char *)pos)); + } else { result = Py_None; @@ -218,33 +223,43 @@ static PyObject *py_leb128_unpack_uleb128(PyObject *self, PyObject *args) static PyObject *py_leb128_unpack_leb128(PyObject *self, PyObject *args) { PyObject *result; /* Valeur à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *bytes; /* Octets brutes transmis */ + Py_ssize_t count; /* Quantité de ces octets */ int ret; /* Bilan de lecture des args. */ + const void *pos; /* Tëte de lecture */ + const void *max; /* Position de lecture maximale*/ leb128_t value; /* Valeur à manipuler */ bool status; /* Bilan de l'opération */ #define LEB128_UNPACK_LEB128_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_leb128, "pbuf", \ + unpack_leb128, "buf", \ METH_VARARGS, py_leb128, \ - "Unpack a signed LEB128 value into a data buffer.\n" \ + "Unpack a signed LEB128 value from bytes.\n" \ "\n" \ - "The *pbuf* argument has to be a" \ - " pychrysalide.common.PackedBuffer instance from where data" \ - " will be read.\n" \ + "\n" \ + "The *buf* argument needs to be bytes with enough data aimed" \ + " to get translated into a signed LEB128 value.\n" \ "\n" \ "The returned value depends on the operation status: *None*" \ - " for failure or a integer value for success." \ + " for failure or a tuple with two items for success: the" \ + " decoded value and the remaining bytes." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "y#", &bytes, &count); if (!ret) return NULL; - status = unpack_leb128(&value, pbuf); + pos = bytes; + max = bytes + count; - if (status) - result = PyLong_FromLongLong(value); + status = unpack_leb128(&value, &pos, max); + if (status) + { + result = PyTuple_New(2); + PyTuple_SetItem(result, 0, PyLong_FromLongLong(value)); + PyTuple_SetItem(result, 1, PyBytes_FromStringAndSize(pos, (char *)max - (char *)pos)); + } else { result = Py_None; diff --git a/plugins/pychrysalide/common/module.c b/plugins/pychrysalide/common/module.c index fa2b4de..c82c7bc 100644 --- a/plugins/pychrysalide/common/module.c +++ b/plugins/pychrysalide/common/module.c @@ -30,7 +30,7 @@ //#include "fnv1a.h" //#include "hex.h" //#include "itoa.h" -//#include "leb128.h" +#include "leb128.h" //#include "packed.h" //#include "pathname.h" //#include "pearson.h" @@ -104,11 +104,11 @@ bool populate_common_module(void) if (result) result = populate_common_module_with_fnv1a(); if (result) result = populate_common_module_with_hex(); if (result) result = populate_common_module_with_itoa(); - if (result) result = populate_common_module_with_leb128(); if (result) result = populate_common_module_with_pathname(); if (result) result = populate_common_module_with_pearson(); */ if (result) result = populate_common_module_with_entropy(); + if (result) result = populate_common_module_with_leb128(); if (result) result = populate_common_module_with_xdg(); if (result) result = ensure_python_bitfield_is_registered(); diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c index 8142284..34caa12 100644 --- a/plugins/pychrysalide/constants.c +++ b/plugins/pychrysalide/constants.c @@ -25,7 +25,6 @@ #include "constants.h" -#include <arch/archbase.h> #include <common/datatypes.h> diff --git a/plugins/pychrysalide/constants.h b/plugins/pychrysalide/constants.h index 332afe0..151f1eb 100644 --- a/plugins/pychrysalide/constants.h +++ b/plugins/pychrysalide/constants.h @@ -46,6 +46,9 @@ int convert_to_source_endian(PyObject *, void *); /* Tente de convertir en constante MemoryDataSize. */ int convert_to_memory_data_size(PyObject *, void *); +#define cast_memory_data_size_to_python(v) \ + cast_with_constants_group_from_module("pychrysalide", "MemoryDataSize", v) + #endif /* _PLUGINS_PYCHRYSALIDE_CONSTANTS_H */ diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c new file mode 100644 index 0000000..08866cb --- /dev/null +++ b/plugins/pychrysalide/convert.c @@ -0,0 +1,120 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.c - conversion d'arguments en éléments usuels externes + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "convert.h" + + +#include <assert.h> +#include <pygobject.h> +#ifndef NDEBUG +# include <stdbool.h> +#endif +#include <gio/gio.h> + + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance GSettings. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gsettings(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + GType type; /* Type obtenu ou 0 */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type); + + if (result == 1) + { + type = pyg_type_from_object(arg); + + if (type != G_TYPE_SETTINGS) + result = 0; + + } + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GSetting instance"); + break; + + case 1: + *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance GSettings ou NULL. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gsettings_or_none(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + if (arg == Py_None) + { + *((GSettings **)dst) = NULL; + result = 1; + } + + else + result = convert_to_gsettings(arg, dst); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/translate.h b/plugins/pychrysalide/convert.h index facffd2..86d7528 100644 --- a/plugins/pychrysalide/plugins/translate.h +++ b/plugins/pychrysalide/convert.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * translate.h - prototypes pour la conversion de structures liées aux greffons en objets Python + * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,20 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H +#ifndef _PLUGINS_PYCHRYSALIDE_CONVERT_H +#define _PLUGINS_PYCHRYSALIDE_CONVERT_H #include <Python.h> -#include <plugins/plugin-def.h> +/* Tente de convertir en instance GSettings. */ +int convert_to_gsettings(PyObject *, void *); +/* Tente de convertir en instance GSettings ou NULL. */ +int convert_to_gsettings_or_none(PyObject *, void *); -/* Traduit une description d'interface de greffon. */ -PyObject *translate_plugin_interface_to_python(const plugin_interface *); - -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */ diff --git a/plugins/pychrysalide/core-int.h b/plugins/pychrysalide/core-int.h new file mode 100644 index 0000000..2b8fcc8 --- /dev/null +++ b/plugins/pychrysalide/core-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-int.h - prototypes internes pour le plugin permettant des extensions en Python + * + * Copyright (C) 2025 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_CORE_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_INT_H + + +#include "core.h" + + +#include <plugins/native-int.h> + + + +/* Greffon natif pour la liaison Python de Chrysalide (instance) */ +struct _GPyChrysalidePlugin +{ + GNativePlugin parent; /* A laisser en premier */ + + PyObject *py_module; /* Réceptacle de chargement */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide (classe) */ +struct _GPyChrysalidePluginClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_INT_H */ diff --git a/plugins/pychrysalide/core-ui-int.h b/plugins/pychrysalide/core-ui-int.h new file mode 100644 index 0000000..7cecc13 --- /dev/null +++ b/plugins/pychrysalide/core-ui-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui-int.h - prototypes internes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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_CORE_UI_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H + + +#include "core-ui.h" + + +#include "core-int.h" + + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (instance) */ +struct _GPyChrysalidePluginUI +{ + GPyChrysalidePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (classe) */ +struct _GPyChrysalidePluginUIClass +{ + GPyChrysalidePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H */ diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c new file mode 100644 index 0000000..0ea15df --- /dev/null +++ b/plugins/pychrysalide/core-ui.c @@ -0,0 +1,496 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.c - plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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 "core-ui.h" + + +#include <stdbool.h> + + +#include <i18n.h> +#include <plugins/manager-int.h> +#include <plugins/self.h> + + +#include "bindings.h" +#include "core-ui-int.h" +#include "arch/module-ui.h" +#include "glibext/module-ui.h" +#include "gtkext/module.h" +//#include "gui/module.h" + + + +/* Note la nature du chargement */ +static bool _standalone = true; + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *); + +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_pychrysalide_plugin_ui_plugin_manager_interface_init(GPluginManagerInterface *); + +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *); + +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_ui_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_ui_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_ui_enable(GPluginModule *); + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Prend acte du chargement de l'ensemble des greffons natifs. */ +static void g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event(GPluginManager *); + + + +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/*Ajoute des modules UI aux extensions Python. */ +static bool add_python_ui_modules(PyObject *); + +/* Inscrit les défintions des objets UI Python de Chrysalide. */ +static bool populate_python_modules_ui(bool); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G_TYPE_PYCHRYSALIDE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_ui_plugin_manager_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_ui_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = g_pychrysalide_plugin_ui_dispose; + object->finalize = g_pychrysalide_plugin_ui_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = g_pychrysalide_plugin_ui_enable; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_plugin_manager_interface_init(GPluginManagerInterface *iface) +{ + iface->handle_native = g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support Python. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module) +{ + GPyChrysalidePluginUI *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL); + + if (!g_pychrysalide_plugin_ui_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un greffon de support Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + + result = g_pychrysalide_plugin_create(G_PYCHRYSALIDE_PLUGIN(plugin), module); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de l'activation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_pychrysalide_plugin_ui_enable(GPluginModule *plugin) +{ + bool result; /* Bilan à retourner */ + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + int ret; /* Bilan de préparatifs */ + + _standalone = false; + + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); + + /* Chargement du module pour Python */ + + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalideui); + + if (ret == -1) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); + + result = false; + goto done; + + } + + Py_Initialize(); + + gstate = PyGILState_Ensure(); + + pychr_plugin->py_module = PyImport_ImportModule("pychrysalide"); + + /** + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. + * + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... + * + * En mode autonome, le shell Python remonte bien l'erreur par contre. + */ + + // TODO : check (2025) + + result = (pychr_plugin->py_module != NULL); + + PyGILState_Release(gstate); + + done: + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_handle_native_plugins_loaded_event(GPluginManager *manager) +{ + GPluginModule *plugin; /* Version de base du greffon */ +#ifdef DISCARD_LOCAL + char *edir; /* Répertoire de base effectif */ + DIR *dir; /* Répertoire à parcourir */ +#endif + GPluginManagerInterface *iface; /* Interface utilisée */ + GPluginManagerInterface *parent_iface; /* Interface parente */ + + plugin = G_PLUGIN_MODULE(manager); + + /* Définition des zones d'influence */ + +#ifndef DISCARD_LOCAL + + extend_python_path(plugin, PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "pythonui"); + +#else + + edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "pythonui"); + + dir = opendir(edir); + + if (dir != NULL) + { + closedir(dir); + + extend_python_path(plugin, edir); + + } + + free(edir); + +#endif + + /* Chargements des extensions Python */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + parent_iface = g_type_interface_peek_parent(iface); + + parent_iface->handle_native(manager); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : super = module dont la définition est à compléter. * +* * +* Description : Ajoute des modules UI aux extensions Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool add_python_ui_modules(PyObject *super) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = add_gtkext_module(super); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : modify = autorisation de motification du type GObject. * +* * +* Description : Inscrit les défintions des objets UI Python de Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_python_modules_ui(bool modify) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (!modify) + { + if (result) result = populate_arch_module_ui(); + if (result) result = populate_glibext_module_ui(); + + } + + else + { + if (result) result = populate_gtkext_module(); + //if (result) result = populate_gui_module(); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point d'entrée pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyMODINIT_FUNC PyInit_pychrysalideui(void) +{ + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ + + details.standalone = _standalone; + + details.add_extra = add_python_ui_modules; + details.populate_extra = populate_python_modules_ui; + details.create_self = g_pychrysalide_plugin_ui_new; + + result = init_python_pychrysalide_module(&details); + + return result; + +} diff --git a/plugins/pychrysalide/core-ui.h b/plugins/pychrysalide/core-ui.h new file mode 100644 index 0000000..658aa19 --- /dev/null +++ b/plugins/pychrysalide/core-ui.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.h - prototypes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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_CORE_UI_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <glibext/helpers.h> +#include <plugins/plugin.h> + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN_UI (g_pychrysalide_plugin_ui_get_type()) + +DECLARE_GTYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G, PYCHRYSALIDE_PLUGIN_UI); + + +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *); + + + +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalideui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_H */ diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 8d69933..0fea9c4 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,20 +21,18 @@ */ +#include "core.h" + + #undef NO_IMPORT_PYGOBJECT #include <pygobject.h> #define NO_IMPORT_PYGOBJECT -#include "core.h" - - #include <assert.h> #include <errno.h> #include <malloc.h> -#include <pygobject.h> #include <stdarg.h> -#include <stdio.h> #include <stdbool.h> #include <string.h> #include <unistd.h> @@ -42,770 +40,467 @@ #include <i18n.h> #include <gleak.h> -#include <common/cpp.h> #include <common/environment.h> #include <common/extstr.h> #include <core/core.h> #include <core/logs.h> #include <core/paths.h> -#include <plugins/pglist.h> +#include <plugins/manager-int.h> +#include <plugins/plugin.h> #include <plugins/self.h> #include "access.h" -#include "constants.h" -#include "helpers.h" -#include "star.h" -#include "strenum.h" -#include "struct.h" -#include "analysis/module.h" -#include "arch/module.h" -#include "common/module.h" -#include "core/module.h" -#include "glibext/module.h" -/* #include "debug/module.h" */ -#include "format/module.h" -/* #ifdef INCLUDE_GTK_SUPPORT */ -/* # include "gtkext/module.h" */ -/* # include "gui/module.h" */ -/* #endif */ -/* #include "mangling/module.h" */ -#include "plugins/module.h" -#include "plugins/plugin.h" - - - -DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Chrysalide bindings to Python", - PACKAGE_VERSION, CHRYSALIDE_WEBSITE("api/python/pychrysalide"), - NO_REQ, AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT, - PGA_NATIVE_PLUGINS_LOADED, PGA_TYPE_BUILDING)); +#include "bindings.h" +#include "core-int.h" + /* Note la nature du chargement */ static bool _standalone = true; -/* Réceptacle pour le chargement forcé */ -static PyObject *_chrysalide_module = NULL; -/* Fournit la révision du programme global. */ -static PyObject *py_chrysalide_revision(PyObject *, PyObject *); +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ -/* Fournit la version du programme global. */ -static PyObject *py_chrysalide_version(PyObject *, PyObject *); -/* Fournit la version du greffon pour Python. */ -static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *); -/* Détermine si l'interpréteur lancé est celui pris en compte. */ -static bool is_current_abi_suitable(void); +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *); -/* Assure une pleine initialisation des objets de Python-GI. */ -static bool install_metaclass_for_python_gobjects(void); +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *); -/* Définit la version attendue de GTK à charger dans Python. */ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *); -#endif +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_enable(GPluginModule *); -/* Point de sortie pour l'initialisation de Python. */ -static void PyExit_pychrysalide(void); +/* Prend acte de la désactivation du greffon. */ +static bool g_pychrysalide_plugin_disable(GPluginModule *); -/* Complète les chemins de recherches de Python. */ -static void extend_python_path(const char *); + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Crée un greffon à partir de code Python. */ +static GPluginModule *create_python_plugin(const char *, const char *); /* Charge autant de greffons composés en Python que possible. */ static void load_python_plugins(GPluginModule *); -/* Efface un type Python pour greffon de la mémoire. */ -static void free_native_plugin_type(PyTypeObject *); +/* Prend acte du chargement de l'ensemble des greffons natifs. */ +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPluginManager *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePlugin, g_pychrysalide_plugin, G_TYPE_NATIVE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_plugin_manager_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_new); + /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : class = classe à initialiser. * * * -* Description : Fournit la révision du programme global. * +* Description : Initialise la classe des greffons de support Python. * * * -* Retour : Numéro de révision. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *class) { - PyObject *result; /* Valeur à retourner */ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ -#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ -( \ - revision, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the revision number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: 'r1665'." \ -) + object = G_OBJECT_CLASS(class); - result = PyUnicode_FromString("r" XSTR(REVISION)); + object->dispose = g_pychrysalide_plugin_dispose; + object->finalize = g_pychrysalide_plugin_finalize; - return result; + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = g_pychrysalide_plugin_enable; + plugin->disable = g_pychrysalide_plugin_disable; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Fournit la version du programme global. * +* Description : Procède à l'initialisation de l'interface de gestion. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface) { - PyObject *result; /* Valeur à retourner */ - int major; /* Numéro de version majeur */ - int minor; /* Numéro de version mineur */ - int revision; /* Numéro de révision */ - char version[16]; /* Conservation temporaire */ + iface->handle_native = g_pychrysalide_plugin_handle_native_plugins_loaded_event; -#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '1.6.65'." \ -) +} - major = REVISION / 1000; - minor = (REVISION - (major * 1000)) / 100; - revision = REVISION % 100; - snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - result = PyUnicode_FromString(version); +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); - return result; + plugin->py_module = NULL; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Fournit la version du greffon pour Python. * +* Description : Supprime toutes les références externes. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_dispose(GObject *object) { - PyObject *result; /* Valeur à retourner */ - char version[16]; /* Conservation temporaire */ - -#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - mod_version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide module for Python.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '0.1.0'." \ -) - - snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version); - - result = PyUnicode_FromString(version); - - return result; + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : true si l'exécution peut se poursuivre, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool is_current_abi_suitable(void) +static void g_pychrysalide_plugin_finalize(GObject *object) { - bool result; - int fds[2]; - int ret; - char cmds[128]; - char content[64]; - ssize_t got; - -#define GRAB_ABI_FLAGS_IN_PYTHON \ - "import sys" "\n" \ - "import os" "\n" \ - "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ - "os.write(%d, data)" "\n" - - result = false; - - ret = pipe(fds); - if (ret == -1) - { - perror("pipe()"); - return false; - } + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(object); - snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); +} - ret = PyRun_SimpleString(cmds); - if (ret != 0) goto icas_exit; - got = read(fds[0], content, sizeof(content)); - if (got < 0) - { - perror("read()"); - goto icas_exit; - } - - content[got] = '\0'; +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support Python. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ - result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); +GPluginModule *g_pychrysalide_plugin_new(GModule *module) +{ + GPyChrysalidePlugin *result; /* Structure à retourner */ - icas_exit: + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL); - if (!result) - PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ - "the ones of the Python library used during the module compilation."); + if (!g_pychrysalide_plugin_create(result, module)) + g_clear_object(&result); - return result; + return G_PLUGIN_MODULE(result); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * * * -* Description : Assure une pleine initialisation des objets de Python-GI. * +* Description : Met en place un module pour un greffon de support Python. * * * * Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : Le transfert de propriétée du module est total. * * * ******************************************************************************/ -static bool install_metaclass_for_python_gobjects(void) +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module) { bool result; /* Bilan à retourner */ - PyObject *gi_types_mod; /* Module Python-GObject */ - - /** - * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), - * qui fait appel à create_python_plugin(). Une instance y est construite via un - * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad - * dans le fichier __init__.py présent dans chaque module d'extension. - * - * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique - * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction - * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. - * Le code de cette dernière comprend notamment la portion suivante : - * - * [...] - * Py_SET_TYPE(type, PyGObject_MetaType); - * [...] - * if (PyType_Ready(type) < 0) { - * g_warning ("couldn't make the type `%s' ready", type->tp_name); - * return; - * } - * [...] - * - * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c - * et commence par : - * - * int PyType_Ready(PyTypeObject *type) - * { - * if (type->tp_flags & Py_TPFLAGS_READY) { - * assert(_PyType_CheckConsistency(type)); - * return 0; - * } - * [...] - * } - * - * La vérification de cohérencce commence par analyser le type et son propre - * type : - * - * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : - * - * int _PyType_CheckConsistency(PyTypeObject *type) - * { - * [...] - * CHECK(!_PyObject_IsFreed((PyObject *)type)); - * [...] - * } - * - * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : - * - * int _PyObject_IsFreed(PyObject *op) - * { - * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { - * return 1; - * } - * - * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. - * - * Or le type du type est écrasé dans la fonction pygobject_register_class() - * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est - * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : - * - * static PyObject * - * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) - * { - * Py_INCREF(metaclass); - * PyGObject_MetaType = metaclass; - * Py_INCREF(metaclass); - * - * Py_SET_TYPE(&PyGObject_Type, metaclass); - * - * Py_INCREF(Py_None); - * return Py_None; - * } - * - * Afin de valider la vérification de _PyType_CheckConsistency() pour les - * modules externes qui entraînent un enregistrement tout en portant le drapeau - * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut - * initialiser au besoin la variable PyGObject_MetaType. - * - * Une ligne suffit donc à enregistrer le type intermédiaire : - * - * from _gi import types - * - * On simule ici une déclaration similaire si nécessaire - */ - - result = false; - if (PyType_CheckExact(&PyGObject_Type)) - { - gi_types_mod = PyImport_ImportModule("gi.types"); + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PyChrysalide", + "Chrysalide bindings to Python", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("api/python/pychrysalide"), + NO_REQ, + module); - result = (PyErr_Occurred() == NULL); + return result; - if (result) - result = (PyType_CheckExact(&PyGObject_Type) == 0); +} - Py_XDECREF(gi_types_mod); - } - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : version = idenfiant de la version de GTK à stipuler. * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Définit la version attendue de GTK à charger dans Python. * +* Description : Prend acte de l'activation du greffon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *version) + +static bool g_pychrysalide_plugin_enable(GPluginModule *plugin) { bool result; /* Bilan à retourner */ - PyObject *gi_mod; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - - result = false; + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + int ret; /* Bilan de préparatifs */ - /** - * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement : - * - * - * PyGIWarning: Gtk was imported without specifying a version first. \ - * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded. - * - */ + _standalone = false; - gi_mod = PyImport_ImportModule("gi"); + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); - if (gi_mod != NULL) - { - args = Py_BuildValue("ss", "Gtk", version); + /* Chargement du module pour Python */ - run_python_method(gi_mod, "require_version", args); + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - result = (PyErr_Occurred() == NULL); + if (ret == -1) + { + g_plugin_module_log_simple_message(plugin, LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); - Py_DECREF(args); - Py_DECREF(gi_mod); + result = false; + goto done; } - return result; - -} -#endif - + Py_Initialize(); -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Point de sortie pour l'initialisation de Python. * -* * -* Retour : ? * -* * -* Remarques : - * -* * -******************************************************************************/ + gstate = PyGILState_Ensure(); -static void PyExit_pychrysalide(void) -{ - assert(_standalone); + pychr_plugin->py_module = PyImport_ImportModule("pychrysalide"); - /* - extern void set_current_project(void *project); + /** + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. + * + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... + * + * En mode autonome, le shell Python remonte bien l'erreur par contre. + */ - set_current_project(NULL); - */ + // TODO : check (2025) -#ifdef TRACK_GOBJECT_LEAKS - remember_gtypes_for_leaks(); -#endif + result = (pychr_plugin->py_module != NULL); - exit_all_plugins(); + PyGILState_Release(gstate); - //unload_all_core_components(true); + done: -#ifdef TRACK_GOBJECT_LEAKS - dump_remaining_gtypes(); -#endif + return result; } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Point d'entrée pour l'initialisation de Python. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : ? * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -#define PYCHRYSALIDE_DOC \ - "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ - "\n" \ - "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ - "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ - "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ - "\n" \ - "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ - "how the API works.\n" \ - "\n" \ - "These plugins are located in the 'plugins/python' directory.\n" \ - "\n" \ - "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." - -PyMODINIT_FUNC PyInit_pychrysalide(void) +static bool g_pychrysalide_plugin_disable(GPluginModule *plugin) { - PyObject *result; /* Module Python à retourner */ - bool status; /* Bilan des inclusions */ - int ret; /* Bilan de préparatifs */ -#ifdef PYTHON_PACKAGE - Dl_info info; /* Informations dynamiques */ -#endif - GPluginModule *self; /* Représentation interne */ - PluginStatusFlags self_flags; /* Fanions à mettre à jour */ - - static PyMethodDef py_chrysalide_methods[] = { - PY_CHRYSALIDE_REVISION_METHOD, - PY_CHRYSALIDE_VERSION_METHOD, - PY_CHRYSALIDE_MOD_VERSION_METHOD, - { NULL } - }; - - static PyModuleDef py_chrysalide_module = { - - .m_base = PyModuleDef_HEAD_INIT, - - .m_name = "pychrysalide", - .m_doc = PYCHRYSALIDE_DOC, - - .m_size = -1, + bool result; /* Bilan à retourner */ + GPyChrysalidePlugin *pychr_plugin; /* Version spécialisée */ + bool standalone; /* Nature du chargement */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - .m_methods = py_chrysalide_methods + result = true; - }; + pychr_plugin = G_PYCHRYSALIDE_PLUGIN(plugin); /** - * Vérification préalable : dans le cas où on est embarqué directement dans - * un interpréteur Python, le module se charge et termine par charger à leur - * tour les différentes extensions trouvées, via load_remaning_plugins() puis - * chrysalide_plugin_on_native_loaded(). - * - * Lesquelles vont très probablement charger le module pychrysalide. - * - * Comme le chargement de ce dernier n'est alors pas encore terminé, - * Python va relancer cette procédure, et register_access_to_python_module() - * va détecter un doublon. + * Le champ plugin->py_module n'est défini que via la fonction + * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque + * le module PyChrysalide est mis en place directement par Python. + * + * L'analyse de ce champ pour retrouver la situation courante est + * plus fiable que celle du champ _standalone, potentiellement + * cohérent dans la version UI du greffon et resté à son état + * initial ici. */ - result = get_access_to_python_module(py_chrysalide_module.m_name); - - if (result != NULL) - { - Py_INCREF(result); - return result; - } - - if (!is_current_abi_suitable()) - goto exit; - - if (pygobject_init(-1, -1, -1) == NULL) - { - PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); - goto exit; - } - - if (!install_metaclass_for_python_gobjects()) - goto exit; + standalone = (pychr_plugin->py_module == NULL); /** - * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, - * à savoir des types convertis de façon incomplète. Par exemple, pour une - * structure GChecksum, le type à l'exécution est : - * - * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] - * - * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] + * Si on se trouve embarqué dans un interpréteur Python, le déchargement + * des greffons est organisé à partir de la fonction PyExit_pychrysalide(), + * directement appelée depuis un contexte Python. * - * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec - * la version debug de Python3 (version de python3-gi : 3.42.2-3) : + * Un verrou n'est alors pas souhaité ici : * - * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() + * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed. * - * Code de reproduction dans un interpréteur classique : + * Avec : * - * import gi - * gi.require_version('GLib', '2.0') - * from gi.repository import GLib + * $ python3d --version + * Python 3.11.2 * */ - if (!import_namespace_from_gi_repository("GLib", "2.0")) - goto exit; -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - if (!set_version_for_gtk_namespace("3.0")) - goto exit; -#endif -#endif - - if (!load_core_components(ACC_GLOBAL_VARS)) - { - PyErr_SetString(PyExc_SystemError, "unable to load all basic components."); - goto exit; - } - - /* Mise en place des fonctionnalités offertes */ - - result = PyModule_Create(&py_chrysalide_module); - - register_access_to_python_module(py_chrysalide_module.m_name, result); + if (!standalone) + gstate = PyGILState_Ensure(); - status = true; + clear_all_accesses_to_python_modules(); - if (status) status = add_features_module(result); + Py_XDECREF(pychr_plugin->py_module); + pychr_plugin->py_module = NULL; - if (status) status = add_analysis_module(result); - if (status) status = add_arch_module(result); - if (status) status = add_common_module(result); - if (status) status = add_glibext_module(result); - if (status) status = add_core_module(result); - /* - if (status) status = add_debug_module(result); - */ - if (status) status = add_format_module(result); - /* -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = add_gtkext_module(result); - if (status) status = add_gui_module(result); -#endif - if (status) status = add_mangling_module(result); - */ - if (status) status = add_plugins_module(result); - - /* - if (status) status = ensure_python_string_enum_is_registered(); - */ - if (status) status = ensure_python_py_struct_is_registered(); - - if (status) status = define_data_types_constants(result); - - if (status) status = populate_analysis_module(); - if (status) status = populate_arch_module(); - if (status) status = populate_glibext_module(); - if (status) status = populate_common_module(); - if (status) status = populate_core_module(); - /* - if (status) status = populate_debug_module(); - */ - if (status) status = populate_format_module(); - /* -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = populate_gtkext_module(); - if (status) status = populate_gui_module(); -#endif - if (status) status = populate_mangling_module(); - */ - if (status) status = populate_plugins_module(); + if (!standalone) + PyGILState_Release(gstate); - if (!status) - { - PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); - Py_DECREF(result); - result = NULL; - goto exit; - } - - if (_standalone) - { - ret = Py_AtExit(PyExit_pychrysalide); + return result; - if (ret == -1) - { - PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); - Py_DECREF(result); - result = NULL; - goto exit; - } +} - /** - * Comme les sources locales sont prioritaires, le fichier "core/global.h" - * du greffon masque la fonction suivante, issue du corps principal du - * programme. - * - * On la déclare donc à la main. - */ - /* - extern void set_batch_mode(void); - - set_batch_mode(); - */ - - /** - * Si cette extension pour Python est chargée depuis un dépôt Python, - * elle ne se trouve pas dans le répertoire classique des extensions et - * n'est donc pas chargée et enregistrée comme attendu. - * - * Cet enregistrement est donc forcé ici. - */ - -#ifdef PYTHON_PACKAGE - - ret = dladdr(__FUNCTION__, &info); - if (ret == 0) - { - LOG_ERROR_DL_N("dladdr"); - Py_DECREF(result); - result = NULL; - goto exit; - } - self = g_plugin_module_new(info.dli_fname); - assert(self != NULL); - register_plugin(self); +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ -#endif - init_all_plugins(false); +/****************************************************************************** +* * +* 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 : - * +* * +******************************************************************************/ - lock_plugin_list_for_reading(); +static GPluginModule *create_python_plugin(const char *modname, const char *filename) +{ + GPluginModule *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ - self = get_plugin_by_name("PyChrysalide", NULL); - assert(self != NULL); + name = PyUnicode_FromString(modname); + if (name == NULL) goto bad_exit; - self_flags = g_plugin_module_get_flags(self); - self_flags &= ~(PSF_FAILURE | PSF_LOADED); - self_flags |= (status ? PSF_LOADED : PSF_FAILURE); + module = PyImport_Import(name); + Py_DECREF(name); - g_plugin_module_override_flags(self, self_flags); + if (module == NULL) goto no_import; - unlock_plugin_list_for_reading(); + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); - load_remaning_plugins(); + if (class == NULL) goto no_class; + if (!PyType_Check(class->ob_type)) goto no_class; - /** - * On laisse fuir ici la référence sur self afin d'avoir - * l'assurance que le greffon se déchargera toujours en dernier. - * - * La fuite mémoire est au final évitée dans PyExit_pychrysalide(). - */ + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto no_instance; - } + result = G_PLUGIN_MODULE(pygobject_get(instance)); - exit: + /** + * L'instance Python et l'objet GLib résultant sont un même PyGObject. + * + * Donc pas besoin de toucher au comptage des références ici, la libération + * se réalisera à la fin, quand l'objet GLib sera libéré. + */ - if (result == NULL && !_standalone) - log_pychrysalide_exception("Loading failed"); + Py_DECREF(module); return result; -} + no_instance: -/****************************************************************************** -* * -* Paramètres : path = chemin supplémentaire pour l'espace de recherche. * -* * -* Description : Complète les chemins de recherches de Python. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); -static void extend_python_path(const char *path) -{ - PyObject *list; /* Liste de chemins à compléter*/ - PyObject *new; /* Nouveau chemin à intégrer */ + no_class: - list = PySys_GetObject("path"); - assert(list != NULL); + if (class == NULL) + log_plugin_simple_message(LMT_ERROR, + _("An error occured when looking for the 'AutoLoad': item not found!")); - new = PyUnicode_FromString(path); - assert(new != NULL); + no_import: - PyList_Append(list, new); + Py_XDECREF(module); - Py_DECREF(new); + log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - add_to_env_var("PYTHONPATH", path, ":"); + bad_exit: + + return NULL; } @@ -824,49 +519,18 @@ static void extend_python_path(const char *path) static void load_python_plugins(GPluginModule *plugin) { -#ifdef DISCARD_LOCAL - char *edir; /* Répertoire de base effectif */ -#endif - DIR *dir; /* Répertoire à parcourir */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ char *paths; /* Emplacements de greffons */ char *save; /* Sauvegarde pour ré-entrance */ char *path; /* Chemin à fouiller */ + DIR *dir; /* Répertoire à parcourir */ struct dirent *entry; /* Elément trouvé */ char *modname; /* Nom du module pour Python */ char *filename; /* Chemin d'accès reconstruit */ GPluginModule *pyplugin; /* Lien vers un grffon Python */ bool status; /* Bilan d'une opération */ - //GGenConfig *config; /* Configuration à charger */ - - /* Définition des zones d'influence */ - -#ifndef DISCARD_LOCAL - - extend_python_path(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python"); -#else - - edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - dir = opendir(edir); - free(edir); - - if (dir != NULL) - { - closedir(dir); - - edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - extend_python_path(edir); - free(edir); - - } - -#endif - - g_plugin_module_log_variadic_message(plugin, LMT_INFO, - _("PYTHONPATH environment variable set to '%s'"), - getenv("PYTHONPATH")); - - /* Chargements des extensions Python */ + gstate = PyGILState_Ensure(); paths = get_env_var("PYTHONPATH"); @@ -879,7 +543,7 @@ static void load_python_plugins(GPluginModule *plugin) dir = opendir(path); if (dir == NULL) { - perror("opendir"); + LOG_ERROR_N("opendir"); continue; } @@ -896,7 +560,7 @@ static void load_python_plugins(GPluginModule *plugin) if (entry == NULL) { if (errno != 0) - perror("readdir"); + LOG_ERROR_N("readdir"); break; @@ -923,8 +587,7 @@ static void load_python_plugins(GPluginModule *plugin) goto done_with_plugin; } - //g_plugin_module_create_config(pyplugin); - + /* status = g_plugin_module_manage(pyplugin, PGA_PLUGIN_LOADED); if (!status) @@ -934,18 +597,19 @@ static void load_python_plugins(GPluginModule *plugin) g_object_unref(G_OBJECT(pyplugin)); goto done_with_plugin; } - - /* - config = g_plugin_module_get_config(pyplugin); - g_generic_config_read(config); - g_object_unref(G_OBJECT(config)); */ g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, _("Loaded the Python plugin found in the '<b>%s</b>' directory"), filename); - _register_plugin(pyplugin); + printf(" -> BUG // %p\n", pyplugin); + + printf(" -> BUG // %u\n", ((GObject *)pyplugin)->ref_count); + + //register_plugin(pyplugin); + + /////////unref_object(pyplugin); done_with_plugin: @@ -960,90 +624,6 @@ static void load_python_plugins(GPluginModule *plugin) free(paths); -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du chargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) -{ - bool result; /* Bilan à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - int ret; /* Bilan de préparatifs */ - - _standalone = false; - - /* Chargement du module pour Python */ - - ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - - if (ret == -1) - { - log_plugin_simple_message(LMT_ERROR, _("Can not extend the existing table of Python built-in modules.")); - result = false; - goto cpi_done; - } - - Py_Initialize(); - - gstate = PyGILState_Ensure(); - - _chrysalide_module = PyImport_ImportModule("pychrysalide"); - - /** - * Pour mémoire, une situation concrête conduisant à un échec : - * le paquet python3-gi-dbg n'est pas installé alors que le - * programme est compilé en mode débogage. - * - * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() - * le laisse rien filtrer... - * - * En mode autonome, le shell Python remonte bien l'erreur par contre. - */ - - result = (_chrysalide_module != NULL); - - PyGILState_Release(gstate); - - cpi_done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du déchargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - - gstate = PyGILState_Ensure(); - - clear_all_accesses_to_python_modules(); - - Py_XDECREF(_chrysalide_module); - PyGILState_Release(gstate); } @@ -1051,30 +631,7 @@ G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : type = informations à libérer de la mémoire. * -* * -* Description : Efface un type Python pour greffon de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void free_native_plugin_type(PyTypeObject *type) -{ - free((char *)type->tp_name); - free((char *)type->tp_doc); - - free(type); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * +* Paramètres : manager = interface à manipuler. * * * * Description : Accompagne la fin du chargement des modules natifs. * * * @@ -1084,308 +641,78 @@ static void free_native_plugin_type(PyTypeObject *type) * * ******************************************************************************/ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action) +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPluginManager *manager) { - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - size_t count; /* Quantité de greffons chargés*/ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire du module */ - GPluginModule **list; /* Ensemble de ces greffons */ - size_t i; /* Boucle de parcours */ - char *name; /* Désignation complète */ - char *doc; /* Description adaptée */ - int ret; /* Bilan d'un appel */ - PyTypeObject *type; /* Nouveau type dynamique */ - - gstate = PyGILState_Ensure(); - - if (action == PGA_NATIVE_PLUGINS_LOADED) - { - /* Intégration des greffons natifs en Python */ - - if (ensure_python_plugin_module_is_registered()) - { - module = get_access_to_python_module("pychrysalide.plugins"); - assert(module != NULL); - - dict = PyModule_GetDict(module); - - list = get_all_plugins(&count); - - for (i = 0; i < count; i++) - { - ret = asprintf(&name, "pychrysalide.plugins.%s", G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - continue; - } - - ret = asprintf(&doc, "Place holder for the native plugin %s documentation", - G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - free(name); - continue; - } - - type = calloc(1, sizeof(PyTypeObject)); - - type->tp_name = name; - type->tp_doc = doc; - type->tp_flags = Py_TPFLAGS_DEFAULT; - type->tp_new = no_python_constructor_allowed; - - if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type)) - g_object_set_data_full(G_OBJECT(list[i]), "python_type", type, - (GDestroyNotify)free_native_plugin_type); - - else - free_native_plugin_type(type); - - } - - if (list != NULL) - free(list); - - } - - /* Chargement des extensions purement Python */ - - load_python_plugins(plugin); - - } - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* type = type d'objet à mettre en place. * -* * -* Description : Crée une instance à partir d'un type dynamique externe. * -* * -* Retour : Instance d'objet gérée par l'extension ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type) -{ - gpointer result; /* Instance à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyTypeObject *pytype; /* Classe Python concernée */ - PyObject *instance; /* Initialisation forcée */ - - result = NULL; - - gstate = PyGILState_Ensure(); - - pytype = pygobject_lookup_class(type); - - if (pytype != NULL) - { - instance = PyObject_CallObject((PyObject *)pytype, NULL); - assert(instance != NULL); - - result = pygobject_get(instance); - - } - - PyGILState_Release(gstate); - - return result; + GPluginModule *plugin; /* Version de base du greffon */ +#ifdef DISCARD_LOCAL + char *edir; /* Répertoire de base effectif */ + DIR *dir; /* Répertoire à parcourir */ +#endif -} + plugin = G_PLUGIN_MODULE(manager); + /* Définition des zones d'influence */ -/****************************************************************************** -* * -* Paramètres : namespace = module particulier à charger à partir de gi. * -* version = idenfiant de la version à stipuler. * -* * -* Description : Charge un module GI dans Python avec une version attendue. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ +#ifndef DISCARD_LOCAL -bool import_namespace_from_gi_repository(const char *namespace, const char *version) -{ - bool result; /* Bilan à retourner */ - PyObject *module; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - int ret; /* Bilan d'une mise en place */ + extend_python_path(plugin, PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python"); - result = false; +#else - /* Sélection d'une version */ + edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); - module = PyImport_ImportModule("gi"); + dir = opendir(edir); - if (module != NULL) + if (dir != NULL) { - args = Py_BuildValue("ss", namespace, version); - - run_python_method(module, "require_version", args); - - result = (PyErr_Occurred() == NULL); + closedir(dir); - Py_DECREF(args); - Py_DECREF(module); + extend_python_path(plugin, edir); } - /* Importation du module visé */ - - if (result) - { - args = PyTuple_New(1); + free(edir); - ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); - if (ret != 0) - { - result = false; - goto args_error; - } +#endif - module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); + /* Chargements des extensions Python */ - result = (module != NULL); + load_python_plugins(plugin); - Py_XDECREF(module); +} - args_error: - Py_DECREF(args); - } - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* +* Paramètres : - * * * -* Description : Présente dans le journal une exception survenue. * +* Description : Point d'entrée pour l'initialisation de Python. * * * -* Retour : - * +* Retour : ? * * * * Remarques : - * * * ******************************************************************************/ -void log_pychrysalide_exception(const char *prefix, ...) +PyMODINIT_FUNC PyInit_pychrysalide(void) { - va_list ap; /* Compléments argumentaires */ - char *msg; /* Message complet à imprimer */ - 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 */ - - assert(PyGILState_Check() == 1); - - if (PyErr_Occurred()) - { - /* Base de la communication */ - - va_start(ap, prefix); - - vasprintf(&msg, prefix, ap); - - va_end(ap); - - /* Détails complémentaires */ - - PyErr_Fetch(&err_type, &err_value, &err_traceback); - - PyErr_NormalizeException(&err_type, &err_value, &err_traceback); - - if (err_traceback == NULL) - { - err_traceback = Py_None; - Py_INCREF(err_traceback); - } - - PyException_SetTraceback(err_value, err_traceback); - - if (err_value == NULL) - msg = stradd(msg, _(": no extra information is provided...")); - - else - { - err_string = PyObject_Str(err_value); - err_msg = PyUnicode_AsUTF8(err_string); + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ - msg = stradd(msg, ": "); - msg = stradd(msg, err_msg); + details.standalone = _standalone; - Py_DECREF(err_string); + details.add_extra = NULL; + details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_new; - } + result = init_python_pychrysalide_module(&details); - /** - * Bien que la documentation précise que la fonction PyErr_Fetch() - * transfère la propritété des éléments retournés, la pratique - * montre que le programme plante à la terminaison en cas d'exception. - * - * C'est par exemple le cas quand un greffon Python ne peut se lancer - * correctement ; l'exception est alors levée à partir de la fonction - * create_python_plugin() et le plantage intervient en sortie d'exécution, - * au moment de la libération de l'extension Python : - * - * ==14939== Jump to the invalid address stated on the next line - * ==14939== at 0x1A8FCBC9: ??? - * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) - * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) - * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) - * ==14939== by 0x10AD19: main (main.c:440) - * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd - * - * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un - * appel à PyErr_PrintEx(0) ne change rien. - * - * La seule différence de l'instruction set_sys_last_vars réside en quelques - * lignes dans le code de l'interpréteur Python : - * - * if (set_sys_last_vars) { - * _PySys_SetObjectId(&PyId_last_type, exception); - * _PySys_SetObjectId(&PyId_last_value, v); - * _PySys_SetObjectId(&PyId_last_traceback, tb); - * } - * - * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? - * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence - * à ces éléments. - * - * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). - */ - - PySys_SetObject("last_type", err_type); - PySys_SetObject("last_value", err_value); - PySys_SetObject("last_traceback", err_traceback); - - Py_XDECREF(err_traceback); - Py_XDECREF(err_value); - Py_XDECREF(err_type); - - log_plugin_simple_message(LMT_ERROR, msg); - - free(msg); - - } + return result; } diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h index 5d25d3d..60c6c93 100644 --- a/plugins/pychrysalide/core.h +++ b/plugins/pychrysalide/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour le plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -35,31 +35,29 @@ #include <Python.h> +#include <glibext/helpers.h> #include <plugins/plugin.h> -#include <plugins/plugin-int.h> -/* Point d'entrée pour l'initialisation de Python. */ -PyMODINIT_FUNC PyInit_pychrysalide(void); +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN (g_pychrysalide_plugin_get_type()) -/* Prend acte du chargement du greffon. */ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); +DECLARE_GTYPE(GPyChrysalidePlugin, g_pychrysalide_plugin, G, PYCHRYSALIDE_PLUGIN); -/* Prend acte du déchargement du greffon. */ -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *); -/* Accompagne la fin du chargement des modules natifs. */ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, PluginAction); +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_new(GModule *); -/* Crée une instance à partir d'un type dynamique externe. */ -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType); -/* Charge un module GI dans Python avec une version attendue. */ -bool import_namespace_from_gi_repository(const char *, const char *); -/* Présente dans le journal une exception survenue. */ -void log_pychrysalide_exception(const char *, ...); +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalide(void); diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c index 7d05578..f0d3d6b 100644 --- a/plugins/pychrysalide/format/executable.c +++ b/plugins/pychrysalide/format/executable.c @@ -47,9 +47,9 @@ /* Initialise la classe des formats exécutables. */ -static void py_executable_format_init_gclass(GExecutableFormatClass *, gpointer); +static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT, py_executable_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_executable_format_init(PyObject *, PyObject *, PyObject *); @@ -95,23 +95,25 @@ static PyObject *py_executable_format_get_portions(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats exécutables. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_executable_format_init_gclass(GExecutableFormatClass *class, gpointer unused) +static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass) { - class->get_machine = py_executable_format_get_target_machine_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper); - class->get_main_addr = py_executable_format_get_main_address_wrapper; - class->refine_portions = py_executable_format_refine_portions_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper); + PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper); + + return 0; } @@ -784,6 +786,8 @@ bool ensure_python_executable_format_is_registered(void) if (!ensure_python_program_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c index 5df2a8f..856c087 100644 --- a/plugins/pychrysalide/format/known.c +++ b/plugins/pychrysalide/format/known.c @@ -42,9 +42,9 @@ /* Initialise la classe des descriptions de fichier binaire. */ -static void py_known_format_init_gclass(GKnownFormatClass *, gpointer); +static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT, py_known_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_known_format_init(PyObject *, PyObject *, PyObject *); @@ -84,23 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats connus. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused) +static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass) { - class->get_key = py_known_format_get_key_wrapper; - class->get_desc = py_known_format_get_description_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper); - class->analyze = py_known_format_analyze_wrapper; + PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper); + + return 0; } @@ -635,6 +637,8 @@ bool ensure_python_known_format_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/program.c b/plugins/pychrysalide/format/program.c index 28c1540..57a359a 100644 --- a/plugins/pychrysalide/format/program.c +++ b/plugins/pychrysalide/format/program.c @@ -55,9 +55,9 @@ /* Initialise la classe des formats de programmes. */ -static void py_program_format_init_gclass(GProgramFormatClass *, gpointer); +static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT, py_program_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_program_format_init(PyObject *, PyObject *, PyObject *); @@ -66,7 +66,7 @@ static int py_program_format_init(PyObject *, PyObject *, PyObject *); static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *); /* Fournit l'emplacement d'une section donnée. */ -static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *); +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *); @@ -86,7 +86,7 @@ static PyObject *py_program_format_has_flag(PyObject *, PyObject *); /* Fournit l'emplacement d'une section donnée. */ -static PyObject *py_program_format_get_section_range_by_name(PyObject *, PyObject *); +static PyObject *py_program_format_find_section_range_by_name(PyObject *, PyObject *); #if 0 @@ -140,21 +140,23 @@ static PyObject *py_program_format_get_errors(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats de programmes. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_program_format_init_gclass(GProgramFormatClass *class, gpointer unused) +static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass) { - class->get_endian = py_program_format_get_endianness_wrapper; - class->get_range_by_name = py_program_format_get_section_range_by_name_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper); + PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper); + + return 0; } @@ -188,7 +190,7 @@ static int py_program_format_init(PyObject *self, PyObject *args, PyObject *kwds "* pychrysalide.format.ProgramFormat._get_endianness().\n" \ "\n" \ "Other optional method may be defined for new classes:\n" \ - "* pychrysalide.format.ProgramFormat._get_section_range_by_name().\n" \ + "* pychrysalide.format.ProgramFormat._find_section_range_by_name().\n" \ "\n" \ "Calls to the *__init__* constructor of this abstract object expect" \ " only one argument: a binary content, provided as a" \ @@ -295,7 +297,7 @@ static SourceEndian py_program_format_get_endianness_wrapper(const GProgramForma * * ******************************************************************************/ -static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range) +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -303,15 +305,15 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo PyObject *pyret; /* Valeur retournée */ int ret; /* Bilan d'une conversion */ -#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _get_section_range_by_name_wrapper, "$self, name", \ - METH_VARARGS, \ - "Abstract method used to compute the area of a section identified by" \ - " its name.\n" \ - "\n" \ - "The expected returned value is a pychrysalide.arch.mrange instance or" \ - " *None* in case of failure." \ +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _find_section_range_by_name_wrapper, "$self, name", \ + METH_VARARGS, \ + "Abstract method used to compute the area of a section identified by" \ + " its name.\n" \ + "\n" \ + "The expected returned value is a pychrysalide.arch.mrange instance or" \ + " *None* in case of failure." \ ) result = false; @@ -320,9 +322,9 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo pyobj = pygobject_new(G_OBJECT(format)); - if (has_python_method(pyobj, "_get_section_range_by_name")) + if (has_python_method(pyobj, "_find_section_range_by_name")) { - pyret = run_python_method(pyobj, "_get_section_range_by_name", NULL); + pyret = run_python_method(pyobj, "_find_section_range_by_name", NULL); if (pyret != NULL) { @@ -520,7 +522,7 @@ static PyObject *py_program_format_has_flag(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyObject *args) +static PyObject *py_program_format_find_section_range_by_name(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ const char *name; /* Nom de section ciblée */ @@ -529,9 +531,9 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO mrange_t range; /* Emplacement obtenu ? */ bool status; /* Bilan de l'opération */ -#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \ ( \ - get_section_range_by_name, "$self, name, /", \ + find_section_range_by_name, "$self, name, /", \ METH_VARARGS, py_program_format, \ "Compute the area of a section identified by its name.\n" \ "\n" \ @@ -546,7 +548,7 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO format = G_PROGRAM_FORMAT(pygobject_get(self)); - status = g_program_format_get_section_range_by_name(format, name, &range); + status = g_program_format_find_section_range_by_name(format, name, &range); if (status) result = build_from_internal_mrange(&range); @@ -1194,8 +1196,8 @@ PyTypeObject *get_python_program_format_type(void) { static PyMethodDef py_program_format_methods[] = { PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER, - PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER, - PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD, /* PROGRAM_FORMAT_SET_FLAG_METHOD, PROGRAM_FORMAT_UNSET_FLAG_METHOD, @@ -1273,6 +1275,8 @@ bool ensure_python_program_format_is_registered(void) if (!ensure_python_known_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 8b021bb..6d3d746 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -1,16 +1,11 @@ -noinst_LTLIBRARIES = libpychrysaglibext.la +noinst_LTLIBRARIES = libpychrysaglibext.la libpychrysaglibextui.la # libpychrysaglibext_la_SOURCES = \ # binarycursor.h binarycursor.c \ # buffercache.h buffercache.c \ -# bufferline.h bufferline.c \ -# comparison.h comparison.c \ # configuration.h configuration.c \ -# linecursor.h linecursor.c \ -# linegen.h linegen.c \ -# module.h module.c \ -# singleton.h singleton.c +# linecursor.h linecursor.c # if BUILD_GTK_SUPPORT @@ -22,9 +17,17 @@ noinst_LTLIBRARIES = libpychrysaglibext.la # endif libpychrysaglibext_la_SOURCES = \ + comparable.h comparable.c \ constants.h constants.c \ + hashable.h hashable.c \ module.h module.c \ + objhole.h objhole.c \ portion.h portion.c \ + secstorage.h secstorage.c \ + serialize.h serialize.c \ + singleton.h singleton.c \ + storage.h storage.c \ + strbuilder.h strbuilder.c \ work.h work.c \ workqueue.h workqueue.c @@ -32,6 +35,16 @@ libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT +libpychrysaglibextui_la_SOURCES = \ + bufferline.h bufferline.c \ + constants-ui.h constants-ui.c \ + generator.h generator.c \ + module-ui.h module-ui.c + +libpychrysaglibextui_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT + + devdir = $(includedir)/chrysalide/$(subdir) -dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) +dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) $(libpychrysaglibextui_la_SOURCES:%c=) diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c index 09404bc..c5b3664 100644 --- a/plugins/pychrysalide/glibext/bufferline.c +++ b/plugins/pychrysalide/glibext/bufferline.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * bufferline.c - équivalent Python du fichier "glibext/bufferline.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,25 +26,38 @@ #include <assert.h> -#include <malloc.h> +//#include <malloc.h> #include <pygobject.h> -#include <i18n.h> -#include <glibext/bufferline.h> -#include <plugins/dt.h> +#include <glibext/bufferline-int.h> -#include "constants.h" +//#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../arch/vmpa.h" +//#include "../arch/vmpa.h" +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(buffer_line, G_TYPE_BUFFER_LINE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_buffer_line_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + /* Accompagne la création d'une instance dérivée en Python. */ static PyObject *py_buffer_line_new(PyTypeObject *, PyObject *, PyObject *); +#if 0 + /* Ajoute du texte à formater dans une ligne donnée. */ static PyObject *py_buffer_line_append_text(PyObject *, PyObject *); @@ -54,29 +67,34 @@ static PyObject *py_buffer_line_get_text(PyObject *, PyObject *); /* Renseigne sur les propriétés particulières liées à une ligne. */ static PyObject *py_buffer_line_get_flags(PyObject *, void *); +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_buffer_line_init(PyObject *self, PyObject *args, PyObject *kwds) { - 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 */ + unsigned char col_count; /* Qté de colonnes attendues */ + int ret; /* Bilan de lecture des args. */ + GBufferLine *line; /* Ligne en version native */ #define BUFFER_LINE_DOC \ "The BufferLine object is used to display processed data: disassembled" \ @@ -84,51 +102,40 @@ static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " BufferLine()" \ + " BufferLine(col_count)" \ + "\n" \ + " Where *col_count* is a integer value providing the expected number of"\ + " rendering columns." \ "\n" \ "Such objets aim to be created from the Chrysalide core only, and" \ " then get populated on demand. Thus, these lines can be viewed as" \ " cached lines and their properties have to be set through the" \ " pychrysalide.glibext.BufferCache instance which contains them." - /* Validations diverses */ - - base = get_python_buffer_line_type(); + /* Récupération des paramètres */ - if (type == base) - goto simple_way; + ret = PyArg_ParseTuple(args, "B", &col_count); + if (!ret) return -1; - /* Mise en place d'un type dédié */ + /* Initialisation d'un objet GLib */ - first_time = (g_type_from_name(type->tp_name) == 0); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - gtype = build_dynamic_type(G_TYPE_BUFFER_LINE, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); + /* Eléments de base */ - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); + line = G_BUFFER_LINE(pygobject_get(self)); - exit: + if (!g_buffer_line_create(line, col_count)) + return -1; - return result; + return 0; } +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant une ligne de tampon. * @@ -256,6 +263,7 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) return result; } +#endif /****************************************************************************** @@ -273,20 +281,24 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) PyTypeObject *get_python_buffer_line_type(void) { static PyMethodDef py_buffer_line_methods[] = { + /* BUFFER_LINE_APPEND_TEXT_METHOD, { "get_text", py_buffer_line_get_text, METH_VARARGS, "get_text($self, first_col, last_col, markup, /)\n--\n\nProvide the text of a buffer line." }, + */ { NULL } }; static PyGetSetDef py_buffer_line_getseters[] = { + /* { "flags", py_buffer_line_get_flags, NULL, "Current flags of the buffer line.", NULL }, + */ { NULL } }; @@ -304,7 +316,8 @@ PyTypeObject *get_python_buffer_line_type(void) .tp_methods = py_buffer_line_methods, .tp_getset = py_buffer_line_getseters, - .tp_new = py_buffer_line_new + .tp_init = py_buffer_line_init, + .tp_new = py_buffer_line_new, }; @@ -342,11 +355,13 @@ bool ensure_python_buffer_line_is_registered(void) if (!register_class_for_pygobject(dict, G_TYPE_BUFFER_LINE, type)) return false; + /* if (!define_line_segment_constants(type)) return false; if (!define_buffer_line_constants(type)) return false; + */ } diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c new file mode 100644 index 0000000..e4982d7 --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.c @@ -0,0 +1,482 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - équivalent Python du fichier "glibext/comparable.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "comparable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/comparable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *); + +/* Réalise une comparaison étendue entre objets. */ +static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet le statut d'une comparaison effectuée par le parent. */ +static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *); + +/* Effectue une comparaison avec un objet 'ComparableObject'. */ +static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused) +{ +#define COMPARABLE_OBJECT_DOC \ + "The ComparableObject class provides an interface to compare" \ + " objects.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, ComparableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.ComparableObject._compare().\n" + + iface->compare = py_comparable_object_compare_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _compare, "$self, other", \ + METH_VARARGS, \ + "Abstract method allowing to compare two objects implementing" \ + " the interface. This method is used to handle rich comparisons"\ + " automatically.\n" \ + "\n" \ + "The result has to be an integer lesser than, equal to, or" \ + " greater than zero if *self* is found, respectively, to be" \ + " lesser than, to match, or to be greater than *other*.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); + + pyret = run_python_method(pyobj, "_compare", args); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet le statut d'une comparaison effectuée par le parent.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GComparableObject *other; /* Second objet à comparer */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *object; /* Mécanismes natifs */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + int status; /* Bilan d'une comparaison */ + +#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \ +( \ + parent_compare, "$sel, otherf", \ + METH_VARARGS, py_comparable_object, \ + "Provide the comparison status defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a signed integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.ComparableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._compare() function has to be" \ + " used instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other); + if (!ret) return NULL; + + object = G_COMPARABLE_OBJECT(pygobject_get(self)); + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface")); + + result = NULL; + + } + else + { + status = parent_iface->compare(object, other); + + result = PyLong_FromLong(status); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premier object Python à consulter. * +* b = second object Python à consulter. * +* op = type de comparaison menée. * +* * +* Description : Effectue une comparaison avec un objet 'ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *result; /* Bilan à retourner */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *obj_a; /* Instance à manipuler #1 */ + GComparableObject *obj_b; /* Instance à manipuler #2 */ + int status; /* Bilan d'une comparaison */ + + ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type()); + if (!ret) + { + result = Py_NotImplemented; + goto cmp_done; + } + + obj_a = G_COMPARABLE_OBJECT(pygobject_get(a)); + obj_b = G_COMPARABLE_OBJECT(pygobject_get(b)); + + status = g_comparable_object_compare(obj_a, obj_b); + + switch (op) + { + case Py_LT: + result = (status < 0 ? Py_True : Py_False); + break; + + case Py_LE: + result = (status <= 0 ? Py_True : Py_False); + break; + + case Py_EQ: + result = (status == 0 ? Py_True : Py_False); + break; + + case Py_NE: + result = (status != 0 ? Py_True : Py_False); + break; + + case Py_GT: + result = (status > 0 ? Py_True : Py_False); + break; + + case Py_GE: + result = (status >= 0 ? Py_True : Py_False); + break; + + default: + assert(false); + result = Py_NotImplemented; + break; + + } + + cmp_done: + + Py_INCREF(result); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_comparable_object_type(void) +{ + static PyMethodDef py_comparable_object_methods[] = { + COMPARABLE_OBJECT_COMPARE_WRAPPER, + COMPARABLE_OBJECT_PARENT_COMPARE_METHOD, + { NULL } + }; + + static PyGetSetDef py_comparable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_comparable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ComparableObject", + .tp_basicsize = sizeof(PyObject), + + /** + * Une valeur de .tp_richcompare non nulle écarte la définition du + * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type + * dans les préparatifs de la fonction Python inherit_slots(). + * + * Ces préparatifs se poursuivent avec type_ready_set_hash(), + * qui initialise .tp_hash avec PyObject_HashNotImplemented(), + * qui n'est donc pas un comportement par défaut. + * + * Côté PyGObject, la fonction pygobject_inherit_slots() y voit + * une implémentation de .tp_hash personnalisée, ce qui bloque + * la défintion d'autres personnalisations, comme celle de + * l'interface HashableObject. + * + * La valeur nominale nulle est ainsi écartée au préalable ici. + */ + .tp_hash = (hashfunc)_Py_HashPointer, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = COMPARABLE_OBJECT_DOC, + + .tp_richcompare = py_comparable_object_richcompare, + + .tp_methods = py_comparable_object_methods, + .tp_getset = py_comparable_object_getseters + + }; + + return &py_comparable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_comparable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'ComparableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_comparable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'objet comparable. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_comparable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable object"); + break; + + case 1: + *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h new file mode 100644 index 0000000..d4c6ecf --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_comparable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */ +bool ensure_python_comparable_object_is_registered(void); + +/* Tente de convertir en interface d'objet comparable. */ +int convert_to_comparable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */ diff --git a/plugins/pychrysalide/glibext/constants-ui.c b/plugins/pychrysalide/glibext/constants-ui.c new file mode 100644 index 0000000..4101600 --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.c @@ -0,0 +1,131 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.c - ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "constants-ui.h" + + +#include <glibext/bufferline.h> + + +#include "../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : type = type dont le dictionnaire est à compléter. * +* * +* Description : Définit les constantes relatives aux lignes de tampon. * +* * +* Retour : true en cas de succès de l'opération, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool define_buffer_line_constants(PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + PyObject *values; /* Groupe de valeurs à établir */ + + values = PyDict_New(); + + result = add_const_to_group(values, "NONE", BLF_NONE); + if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); + if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); + if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); + if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); + if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); + if (result) result = add_const_to_group(values, "ALL", BLF_ALL); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, + "Optional flags linked to a rendering line."); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en constante BufferLineFlags. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_buffer_line_flags(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + unsigned long value; /* Valeur transcrite */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to BufferLineFlags"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if ((value & BLF_ALL) != value) + { + PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); + result = 0; + } + + else + *((BufferLineFlags *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/constants-ui.h b/plugins/pychrysalide/glibext/constants-ui.h new file mode 100644 index 0000000..6c7bc6e --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.h - prototypes pour l'ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Définit les constantes relatives aux lignes de tampon. */ +bool define_buffer_line_constants(PyTypeObject *); + +/* Tente de convertir en constante BufferLineFlags. */ +int convert_to_buffer_line_flags(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H */ diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c index 90ce8cd..f733cf6 100644 --- a/plugins/pychrysalide/glibext/constants.c +++ b/plugins/pychrysalide/glibext/constants.c @@ -29,7 +29,6 @@ /* #include <i18n.h> -#include <glibext/bufferline.h> #include <glibext/comparison.h> #include <glibext/configuration.h> #include <glibext/linesegment.h> @@ -159,105 +158,6 @@ int convert_to_portion_access_rights(PyObject *arg, void *dst) * * * Paramètres : type = type dont le dictionnaire est à compléter. * * * -* Description : Définit les constantes relatives aux lignes de tampon. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_buffer_line_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - values = PyDict_New(); - - result = add_const_to_group(values, "NONE", BLF_NONE); - if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); - if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); - if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); - if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); - if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); - if (result) result = add_const_to_group(values, "ALL", BLF_ALL); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, - "Optional flags linked to a rendering line."); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en constante BufferLineFlags. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_buffer_line_flags(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur transcrite */ - - result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); - - switch (result) - { - case -1: - /* L'exception est déjà fixée par Python */ - result = 0; - break; - - case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to BufferLineFlags"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - - if ((value & BLF_ALL) != value) - { - PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); - result = 0; - } - - else - *((BufferLineFlags *)dst) = value; - - break; - - default: - assert(false); - break; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * * Description : Définit les constantes relatives aux modes de comparaison. * * * * Retour : true en cas de succès de l'opération, false sinon. * diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h index a950125..c695aa9 100644 --- a/plugins/pychrysalide/glibext/constants.h +++ b/plugins/pychrysalide/glibext/constants.h @@ -39,12 +39,6 @@ int convert_to_portion_access_rights(PyObject *, void *); #if 0 -/* Définit les constantes relatives aux lignes de tampon. */ -bool define_buffer_line_constants(PyTypeObject *); - -/* Tente de convertir en constante BufferLineFlags. */ -int convert_to_buffer_line_flags(PyObject *, void *); - /* Définit les constantes relatives aux modes de comparaison. */ bool define_comparable_item_constants(PyTypeObject *); diff --git a/plugins/pychrysalide/glibext/linegen.c b/plugins/pychrysalide/glibext/generator.c index d7e96fd..3a9a8ab 100644 --- a/plugins/pychrysalide/glibext/linegen.c +++ b/plugins/pychrysalide/glibext/generator.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.c - équivalent Python du fichier "glibext/linegen.h" + * generator.c - équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,21 +22,21 @@ */ -#include "linegen.h" +#include "generator.h" #include <pygobject.h> -#include <glibext/linegen-int.h> +#include <glibext/generator-int.h> #include "bufferline.h" -#include "constants.h" -#include "linecursor.h" +#include "constants-ui.h" +//#include "linecursor.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" +//#include "../analysis/content.h" @@ -44,42 +44,55 @@ /* Procède à l'initialisation de l'interface de génération. */ -static void py_line_generator_interface_init(GLineGeneratorIface *, gpointer *); +static void py_token_generator_interface_init(GTokenGeneratorInterface *, gpointer *); /* Indique le nombre de ligne prêtes à être générées. */ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *); +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *); + +/* Renseigne sur les propriétés liées à un générateur. */ +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *, size_t, size_t); + +/*Description : Etablit dans une ligne de rendu le contenu représenté. */ +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + + +#if 0 + /* Retrouve l'emplacement correspondant à une position donnée. */ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *, gint, size_t, size_t, GLineCursor **); +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *, gint, size_t, size_t, GLineCursor **); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *, size_t, size_t, const GLineCursor *); +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *, size_t, size_t, const GLineCursor *); + + +#endif -/* Renseigne sur les propriétés liées à un générateur. */ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *, size_t, size_t); -/* Imprime dans une ligne de rendu le contenu représenté. */ -static void py_line_generator_print_wrapper(GLineGenerator *, GBufferLine *, size_t, size_t, const GBinContent *); +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ -/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ +/* Renseigne sur les propriétés liées à un générateur. */ +static PyObject *py_token_generator_get_flags(PyObject *, PyObject *); + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args); + +#if 0 /* Retrouve l'emplacement correspondant à une position donnée. */ -static PyObject *py_line_generator_compute_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_compute_cursor(PyObject *, PyObject *); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static PyObject *py_line_generator_contain_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_contain_cursor(PyObject *, PyObject *); -/* Renseigne sur les propriétés liées à un générateur. */ -static PyObject *py_line_generator_get_flags(PyObject *, PyObject *); +#endif -/* Imprime dans une ligne de rendu le contenu représenté. */ -static PyObject *py_line_generator_print(PyObject *, PyObject *); /* Indique le nombre de ligne prêtes à être générées. */ -static PyObject *py_line_generator_get_lines_count(PyObject *, void *); +static PyObject *py_token_generator_get_lines_count(PyObject *, void *); @@ -101,31 +114,33 @@ static PyObject *py_line_generator_get_lines_count(PyObject *, void *); * * ******************************************************************************/ -static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointer *unused) +static void py_token_generator_interface_init(GTokenGeneratorInterface *iface, gpointer *unused) { -#define LINE_GENERATOR_DOC \ - "LineGenerator gives an interface to all objects which aim to produce" \ +#define TOKEN_GENERATOR_DOC \ + "TokenGenerator gives an interface to all objects which aim to produce" \ " content for rendering lines. Such lines can be exported to graphical" \ " interfaces or text files.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, LineGenerator):\n" \ + " class NewImplem(GObject.Object, TokenGenerator):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ - "* pychrysalide.glibext.LineGenerator._count_lines();\n" \ - "* pychrysalide.glibext.LineGenerator._compute_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._contain_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._get_flags();\n" \ - "* pychrysalide.glibext.LineGenerator._print();\n" \ - - iface->count = py_line_generator_count_lines_wrapper; - iface->compute = py_line_generator_compute_cursor_wrapper; - iface->contain = py_line_generator_contain_cursor_wrapper; - iface->get_flags = py_line_generator_get_flags_wrapper; - iface->print = py_line_generator_print_wrapper; + "* pychrysalide.glibext.TokenGenerator._count_lines();\n" \ + "* pychrysalide.glibext.TokenGenerator._get_flags();\n" \ + "* pychrysalide.glibext.TokenGenerator._populate_line();\n" \ + "* pychrysalide.glibext.TokenGenerator._compute_cursor();\n" \ + "* pychrysalide.glibext.TokenGenerator._contain_cursor().\n" + + iface->count = py_token_generator_count_lines_wrapper; + iface->get_flags = py_token_generator_get_flags_wrapper; + iface->populate = py_token_generator_populate_line_wrappper; +#if 0 + iface->compute = py_token_generator_compute_cursor_wrapper; + iface->contain = py_token_generator_contain_cursor_wrapper; +#endif } @@ -142,7 +157,7 @@ static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointe * * ******************************************************************************/ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *generator) +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *generator) { size_t result; /* Décompte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -150,7 +165,7 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ ( \ _count_lines, "$self, /", \ METH_NOARGS, \ @@ -193,61 +208,61 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * -* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat) { + BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _compute_cursor, "$self, x, index, repeat, /", \ + _get_flags, "$self, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to create a new cursor for a given" \ - " location inside displayed lines.\n" \ + "Abstract method used to provide flags for a given rendering" \ + " line.\n" \ "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ + "The result has to be a" \ + " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ ) + result = BLF_NONE; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_compute_cursor")) + if (has_python_method(pyobj, "_get_flags")) { - args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_compute_cursor", args); + pyret = run_python_method(pyobj, "_get_flags", args); if (pyret != NULL) { - ret = convert_to_line_cursor(pyret, cursor); + ret = convert_to_buffer_line_flags(pyret, &result); if (ret != 1) - *cursor = NULL; + result = BLF_NONE; Py_DECREF(pyret); @@ -261,75 +276,66 @@ static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *gener PyGILState_Release(gstate); + return result; + } /****************************************************************************** * * -* Paramètres : generator = générateur à consulter. * +* Paramètres : generator = générateur à utiliser pour l'impression. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* cursor = emplacement à analyser. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) { - int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _contain_cursor, "$self, index, repeat, cursor, /", \ + _populate_line, "$self, index, repeat, line, /", \ METH_VARARGS, \ - "Abstract method used to check the position of a cursor in" \ - " relation to rendering lines.\n" \ + "Abstract method used to generate content into a rendering" \ + " line.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ + "This rendering output is pointed by the *line* argument, which"\ + " is a provided pychrysalide.glibext.BufferLine instance. The" \ + " line *index* and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + " about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_contain_cursor")) + if (has_python_method(pyobj, "_populate_line")) { args = PyTuple_New(3); PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(line))); + //PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); - pyret = run_python_method(pyobj, "_contain_cursor", args); - - if (pyret != NULL) - { - ret = PyLong_Check(pyret); + pyret = run_python_method(pyobj, "_populate_line", args); - if (ret) - result = PyLong_AsLong(pyret); - - Py_DECREF(pyret); - - } + Py_XDECREF(pyret); Py_DECREF(args); @@ -339,69 +345,70 @@ static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *genera PyGILState_Release(gstate); - return result; - } +#if 0 + + /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * +* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *generator, size_t index, size_t repeat) +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) { - BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _get_flags, "$self, index, repeat, /", \ + _compute_cursor, "$self, x, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to provide flags for a given rendering" \ - " line.\n" \ + "Abstract method used to create a new cursor for a given" \ + " location inside displayed lines.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ "\n" \ - "The result has to be a" \ - " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - result = BLF_NONE; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_get_flags")) + if (has_python_method(pyobj, "_compute_cursor")) { - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_get_flags", args); + pyret = run_python_method(pyobj, "_compute_cursor", args); if (pyret != NULL) { - ret = convert_to_buffer_line_flags(pyret, &result); + ret = convert_to_line_cursor(pyret, cursor); if (ret != 1) - result = BLF_NONE; + *cursor = NULL; Py_DECREF(pyret); @@ -415,65 +422,75 @@ static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * -* Paramètres : generator = générateur à utiliser pour l'impression. * -* line = ligne de rendu à compléter. * +* Paramètres : generator = générateur à consulter. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * +* cursor = emplacement à analyser. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) { + int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ + int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _print, "$self, line, index, repeat, content, /", \ + _contain_cursor, "$self, index, repeat, cursor, /", \ METH_VARARGS, \ - "Abstract method used to generate content into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance.\n" \ + "Abstract method used to check the position of a cursor in" \ + " relation to rendering lines.\n" \ "\n" \ "The line index and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the current rendering position.\n" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) + result = 0; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_print")) + if (has_python_method(pyobj, "_contain_cursor")) { - args = PyTuple_New(4); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); - pyret = run_python_method(pyobj, "_print", args); + pyret = run_python_method(pyobj, "_contain_cursor", args); - Py_XDECREF(pyret); + if (pyret != NULL) + { + ret = PyLong_Check(pyret); + + if (ret) + result = PyLong_AsLong(pyret); + + Py_DECREF(pyret); + + } Py_DECREF(args); @@ -483,12 +500,16 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi PyGILState_Release(gstate); + return result; + } +#endif + /* ---------------------------------------------------------------------------------- */ -/* CONNEXION AVEC L'API DE PYTHON */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ /* ---------------------------------------------------------------------------------- */ @@ -497,57 +518,45 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_get_flags(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ - int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - GLineCursor *cursor; /* Curseur nouveau obtenu */ + GTokenGenerator *generator; /* Version native */ + BufferLineFlags flags; /* Propriétés courantes */ -#define LINE_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ -( \ - compute_cursor, "$self, x, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Create a a new cursor for a given location inside displayed" \ - " lines.\n" \ - "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ - "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ +#define TOKEN_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ +( \ + get_flags, "$self, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Get the flags of a given position from the generator.\n" \ + "\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ + "\n" \ + "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ + " value." \ ) - ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); + ret = PyArg_ParseTuple(args, "nn", &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - cursor = g_line_generator_compute_cursor(generator, x, index, repeat); + flags = g_token_generator_get_flags(generator, index, repeat); - if (cursor != NULL) - { - result = pygobject_new(G_OBJECT(cursor)); - g_object_unref(G_OBJECT(cursor)); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); return result; @@ -559,99 +568,108 @@ static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_contain_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args) { - PyObject *result; /* Propriétés à retourner */ + GBufferLine *line; /* Ligne de rendu à compléter */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GLineCursor *cursor; /* Curseur à venir situer */ + GTokenGenerator *generator; /* Version native */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_METHOD PYTHON_METHOD_DEF \ ( \ - contain_cursor, "$self, index, repeat, cursor, /", \ - METH_VARARGS, py_line_generator, \ - "Check the position of a cursor in relation to rendering" \ - " lines.\n" \ + populate_line, "$self, index, repeat, line, /", \ + METH_VARARGS, py_token_generator, \ + "Produce output into a rendering line with optional content.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + "The provided *line* is a pychrysalide.glibext.BufferLine" \ + " instance. The *index* and the number of repetitions (only" \ + " relevant if the generator produces several lines) give" \ + " indications about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_buffer_line, &line); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - status = g_line_generator_contain_cursor(generator, index, repeat, cursor); + g_token_generator_populate_line(generator, index, repeat, line, NULL); - result = PyLong_FromLong(status); - - return result; + Py_RETURN_NONE; } + +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) +static PyObject *py_token_generator_compute_cursor(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ + int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - BufferLineFlags flags; /* Propriétés courantes */ + GTokenGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur nouveau obtenu */ -#define LINE_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ -( \ - get_flags, "$self, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Get the flags of a given position from the generator.\n" \ - "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ - "\n" \ - "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ - " value." \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ +( \ + compute_cursor, "$self, x, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Create a a new cursor for a given location inside displayed" \ + " lines.\n" \ + "\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ + "\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - ret = PyArg_ParseTuple(args, "nn", &index, &repeat); + ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - flags = g_line_generator_get_flags(generator, index, repeat); + cursor = g_token_generator_compute_cursor(generator, x, index, repeat); - result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); + if (cursor != NULL) + { + result = pygobject_new(G_OBJECT(cursor)); + g_object_unref(G_OBJECT(cursor)); + } + else + { + result = Py_None; + Py_INCREF(result); + } return result; @@ -663,50 +681,56 @@ static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_print(PyObject *self, PyObject *args) +static PyObject *py_token_generator_contain_cursor(PyObject *self, PyObject *args) { - GBufferLine *line; /* Ligne de rendu à compléter */ + PyObject *result; /* Propriétés à retourner */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GBinContent *content; /* Contenu binaire associé */ - GLineGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur à venir situer */ int ret; /* Bilan de lecture des args. */ + GTokenGenerator *generator; /* Version native */ + int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_PRINT_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ ( \ - print, "$self, line, index, repeat, content, /", \ - METH_VARARGS, py_line_generator, \ - "Produce output into a rendering line with optional content.\n" \ + contain_cursor, "$self, index, repeat, cursor, /", \ + METH_VARARGS, py_token_generator, \ + "Check the position of a cursor in relation to rendering" \ + " lines.\n" \ "\n" \ - "The provided line is a pychrysalide.glibext.BufferLine" \ - " instance. The index and the number of repetitions (only" \ - " relevant if the generator produces several lines) give" \ - " indications about the current rendering position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) - ret = PyArg_ParseTuple(args, "O&nnO&", convert_to_buffer_line, &line, &index, - &repeat, convert_to_binary_content, &content); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - g_line_generator_print(generator, line, index, repeat, content); + status = g_token_generator_contain_cursor(generator, index, repeat, cursor); - Py_RETURN_NONE; + result = PyLong_FromLong(status); + + return result; } +#endif + /****************************************************************************** * * @@ -721,24 +745,24 @@ static PyObject *py_line_generator_print(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure) +static PyObject *py_token_generator_get_lines_count(PyObject *self, void *closure) { PyObject *result; /* Décompte à retourner */ - GLineGenerator *generator; /* Version native */ + GTokenGenerator *generator; /* Version native */ size_t count; /* Nombre de lignes présentes */ -#define LINE_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ +#define TOKEN_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - lines_count, py_line_generator, \ + lines_count, py_token_generator, \ "Quantity of lines produced by the generator.\n" \ "\n" \ "This number may vary between calls, if a width has changed" \ " for instance." \ ) - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - count = g_line_generator_count_lines(generator); + count = g_token_generator_count_lines(generator); result = PyLong_FromSize_t(count); @@ -759,43 +783,47 @@ static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure * * ******************************************************************************/ -PyTypeObject *get_python_line_generator_type(void) +PyTypeObject *get_python_token_generator_type(void) { - static PyMethodDef py_line_generator_methods[] = { - LINE_GENERATOR_COUNT_LINES_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER, - LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER, - LINE_GENERATOR_GET_FLAGS_WRAPPER, - LINE_GENERATOR_PRINT_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_METHOD, - LINE_GENERATOR_CONTAIN_CURSOR_METHOD, - LINE_GENERATOR_GET_FLAGS_METHOD, - LINE_GENERATOR_PRINT_METHOD, + static PyMethodDef py_token_generator_methods[] = { + TOKEN_GENERATOR_COUNT_LINES_WRAPPER, + TOKEN_GENERATOR_GET_FLAGS_WRAPPER, + TOKEN_GENERATOR_POPULATE_LINE_WRAPPER, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER, + TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER, + */ + TOKEN_GENERATOR_GET_FLAGS_METHOD, + TOKEN_GENERATOR_POPULATE_LINE_METHOD, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD, + TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD, + */ { NULL } }; - static PyGetSetDef py_line_generator_getseters[] = { - LINE_GENERATOR_LINES_COUNT_ATTRIB, + static PyGetSetDef py_token_generator_getseters[] = { + TOKEN_GENERATOR_LINES_COUNT_ATTRIB, { NULL } }; - static PyTypeObject py_line_generator_type = { + static PyTypeObject py_token_generator_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.LineGenerator", + .tp_name = "pychrysalide.glibext.TokenGenerator", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = LINE_GENERATOR_DOC, + .tp_doc = TOKEN_GENERATOR_DOC, - .tp_methods = py_line_generator_methods, - .tp_getset = py_line_generator_getseters, + .tp_methods = py_token_generator_methods, + .tp_getset = py_token_generator_getseters, }; - return &py_line_generator_type; + return &py_token_generator_type; } @@ -804,7 +832,7 @@ PyTypeObject *get_python_line_generator_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'.* +* Description : Prend en charge l'objet 'pychrysalide.....TokenGenerator'. * * * * Retour : Bilan de l'opération. * * * @@ -812,21 +840,21 @@ PyTypeObject *get_python_line_generator_type(void) * * ******************************************************************************/ -bool ensure_python_line_generator_is_registered(void) +bool ensure_python_token_generator_is_registered(void) { - PyTypeObject *type; /* Type Python 'LineGenerator' */ + PyTypeObject *type; /* Type Python 'TokenGenerator'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_line_generator_interface_init, + .interface_init = (GInterfaceInitFunc)py_token_generator_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_line_generator_type(); + type = get_python_token_generator_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -834,7 +862,7 @@ bool ensure_python_line_generator_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_LINE_GENERATOR, type, &info)) + if (!register_interface_for_pygobject(dict, G_TYPE_TOKEN_GENERATOR, type, &info)) return false; } @@ -857,11 +885,11 @@ bool ensure_python_line_generator_is_registered(void) * * ******************************************************************************/ -int convert_to_line_generator(PyObject *arg, void *dst) +int convert_to_token_generator(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_line_generator_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_token_generator_type()); switch (result) { @@ -871,11 +899,11 @@ int convert_to_line_generator(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to line generator"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to token generator"); break; case 1: - *((GLineGenerator **)dst) = G_LINE_GENERATOR(pygobject_get(arg)); + *((GTokenGenerator **)dst) = G_TOKEN_GENERATOR(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/linegen.h b/plugins/pychrysalide/glibext/generator.h index bfad885..b2672a8 100644 --- a/plugins/pychrysalide/glibext/linegen.h +++ b/plugins/pychrysalide/glibext/generator.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.h - prototypes pour l'équivalent Python du fichier "glibext/linegen.h" + * generator.h - prototypes pour l'équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_line_generator_type(void); +PyTypeObject *get_python_token_generator_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'. */ -bool ensure_python_line_generator_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.TokenGenerator'. */ +bool ensure_python_token_generator_is_registered(void); /* Tente de convertir en générateur de lignes. */ -int convert_to_line_generator(PyObject *, void *); +int convert_to_token_generator(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H */ diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/hashable.c index 548f700..c870d55 100644 --- a/plugins/pychrysalide/glibext/comparison.c +++ b/plugins/pychrysalide/glibext/hashable.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - équivalent Python du fichier "glibext/comparison.h" + * hashable.c - équivalent Python du fichier "glibext/hashable.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,38 +22,40 @@ */ -#include "comparison.h" +#include "hashable.h" +#include <assert.h> #include <pygobject.h> -#include <glibext/comparison-int.h> +#include <glibext/hashable-int.h> -#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Procède à l'initialisation de l'interface de comparaison. */ -static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *); +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *); -/* Réalise une comparaison entre objets selon un critère précis. */ -static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint py_hashable_object_hash_wrapper(const GHashableObject *); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Effectue une comparaison avec un objet 'ComparableItem'. */ -static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); +/* Transmet l'empreinte d'un objet calculée par son parent. */ +static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static Py_hash_t py_hashable_object_hash(PyObject *); @@ -67,7 +69,7 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * Paramètres : iface = interface GLib à initialiser. * * unused = adresse non utilisée ici. * * * -* Description : Procède à l'initialisation de l'interface de comparaison. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -75,72 +77,78 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * * ******************************************************************************/ -static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused) +static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused) { - -#define COMPARABLE_ITEM_DOC \ - "ComparableItem provides an interface to compare objects.\n" \ +#define HASHABLE_OBJECT_DOC \ + "The HashableObject class defines a interface ensuring that a" \ + " customized hashing method is available for an object.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, ComparableItem):\n" \ + " class NewImplem(GObject.Object, HashableObject):\n" \ " ...\n" \ - "\n" + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.HashableObject._hash().\n" - iface->cmp_rich = py_comparable_item_compare_rich; + iface->hash = py_hashable_object_hash_wrapper; } /****************************************************************************** * * -* Paramètres : item = premier objet à cnsulter pour une comparaison. * -* other = second objet à cnsulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Réalise une comparaison entre objets selon un critère précis.* +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : true si la comparaison a pu être effectuée, false sinon. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) +static guint py_hashable_object_hash_wrapper(const GHashableObject *object) { - bool result; /* Etat à retourner */ + guint result; /* Valeur à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyitem; /* Objet Python concerné #1 */ - PyObject *pyother; /* Objet Python concerné #2 */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ - result = false; +#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _hash, "$self", \ + METH_NOARGS, \ + "Abstract method computing a hash from an object which is used" \ + " as the default implementation of the __hash__() method.\n" \ + "\n" \ + "The result has to be an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; gstate = PyGILState_Ensure(); - pyitem = pygobject_new(G_OBJECT(item)); - pyother = pygobject_new(G_OBJECT(other)); + pyobj = pygobject_new(G_OBJECT(object)); - pyret = PyObject_RichCompare(pyitem, pyother, op); + pyret = run_python_method(pyobj, "_hash", NULL); if (pyret != NULL) { - ret = PyBool_Check(pyret); + if (PyLong_Check(pyret)) + result = PyLong_AsUnsignedLong(pyret); - if (ret) - { - *status = (pyret == Py_True); - result = true; - } - - Py_DECREF(pyret); + else + PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer")); } - Py_DECREF(pyother); - Py_DECREF(pyitem); + Py_XDECREF(pyret); + + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -157,47 +165,97 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * * * -* Description : Effectue une comparaison avec un objet 'ComparableItem'. * +* Description : Transmet l'empreinte d'un objet calculée par son parent. * * * -* Retour : Bilan de l'opération. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args) { - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GComparableItem *item_a; /* Instance à manipuler #1 */ - GComparableItem *item_b; /* Instance à manipuler #2 */ - bool valid; /* Indication de validité */ - bool status; /* Résultat d'une comparaison */ - - ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type()); - if (!ret) + PyObject *result; /* Valeur à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + guint hash; /* Valeur d'empreitne */ + +#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + parent_hash, "$self", \ + METH_NOARGS, py_hashable_object, \ + "Provide the hash value defined by the interface implementation"\ + " from the object native parent.\n" \ + "\n" \ + "The result is an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.HashableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._hash() function has to be used" \ + " instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) { - result = Py_NotImplemented; - goto cmp_done; + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface")); + + result = NULL; + } + else + { + hash = parent_iface->hash(object); - item_a = G_COMPARABLE_ITEM(pygobject_get(a)); - item_b = G_COMPARABLE_ITEM(pygobject_get(b)); + result = PyLong_FromUnsignedLong(hash); - valid = g_comparable_item_compare_rich(item_a, item_b, op, &status); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} - if (valid) - result = status ? Py_True : Py_False; - else - result = Py_NotImplemented; - cmp_done: +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ - Py_INCREF(result); +static Py_hash_t py_hashable_object_hash(PyObject *self) +{ + Py_hash_t result; /* Empreinte à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + result = g_hashable_object_hash(object); + + UPDATE_RESULT_IF_RAISED_EXCEPTION(-1); return result; @@ -216,44 +274,46 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op * * ******************************************************************************/ -PyTypeObject *get_python_comparable_item_type(void) +PyTypeObject *get_python_hashable_object_type(void) { - static PyMethodDef py_comparable_item_methods[] = { + static PyMethodDef py_hashable_object_methods[] = { + HASHABLE_OBJECT_HASH_WRAPPER, + HASHABLE_OBJECT_PARENT_HASH_METHOD, { NULL } }; - static PyGetSetDef py_comparable_item_getseters[] = { + static PyGetSetDef py_hashable_object_getseters[] = { { NULL } }; - static PyTypeObject py_comparable_item_type = { + static PyTypeObject py_hashable_object_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.ComparableItem", + .tp_name = "pychrysalide.glibext.HashableObject", .tp_basicsize = sizeof(PyObject), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_hash = py_hashable_object_hash, - .tp_doc = COMPARABLE_ITEM_DOC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_richcompare = py_comparable_item_richcompare, + .tp_doc = HASHABLE_OBJECT_DOC, - .tp_methods = py_comparable_item_methods, - .tp_getset = py_comparable_item_getseters, + .tp_methods = py_hashable_object_methods, + .tp_getset = py_hashable_object_getseters, }; - return &py_comparable_item_type; + return &py_hashable_object_type; } /****************************************************************************** * * -* Paramètres : module = module dont la définition est à compléter. * +* Paramètres : - * * * -* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. * +* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. * * * * Retour : Bilan de l'opération. * * * @@ -261,21 +321,21 @@ PyTypeObject *get_python_comparable_item_type(void) * * ******************************************************************************/ -bool ensure_python_comparable_item_is_registered(void) +bool ensure_python_hashable_object_is_registered(void) { - PyTypeObject *type; /* Type Python 'ComparableItem' */ + PyTypeObject *type; /* Type Python 'HashableObject'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init, + .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_comparable_item_type(); + type = get_python_hashable_object_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -283,10 +343,7 @@ bool ensure_python_comparable_item_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info)) - return false; - - if (!define_comparable_item_constants(type)) + if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info)) return false; } @@ -301,7 +358,7 @@ bool ensure_python_comparable_item_is_registered(void) * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * -* Description : Tente de convertir en élément comparable. * +* Description : Tente de convertir en interface d'objet réductible. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * @@ -309,11 +366,11 @@ bool ensure_python_comparable_item_is_registered(void) * * ******************************************************************************/ -int convert_to_comparable_item(PyObject *arg, void *dst) +int convert_to_hashable_object(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type()); switch (result) { @@ -323,11 +380,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object"); break; case 1: - *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg)); + *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h index 79f7092..8583118 100644 --- a/plugins/pychrysalide/glibext/comparison.h +++ b/plugins/pychrysalide/glibext/hashable.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h" + * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_comparable_item_type(void); +PyTypeObject *get_python_hashable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */ -bool ensure_python_comparable_item_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */ +bool ensure_python_hashable_object_is_registered(void); -/* Tente de convertir en élément comparable. */ -int convert_to_comparable_item(PyObject *, void *); +/* Tente de convertir en interface d'objet réductible. */ +int convert_to_hashable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */ diff --git a/plugins/pychrysalide/glibext/module-ui.c b/plugins/pychrysalide/glibext/module-ui.c new file mode 100644 index 0000000..8fa6d0e --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.c @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module-ui.h" + + +#include <assert.h> + + +#include "bufferline.h" +#include "generator.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'glibext' (mode UI). * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_glibext_module_ui(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_token_generator_is_registered(); + + if (result) result = ensure_python_buffer_line_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/glibext/module-ui.h b/plugins/pychrysalide/glibext/module-ui.h new file mode 100644 index 0000000..d91a2fa --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Intègre les objets du module 'glibext' (mode UI). */ +bool populate_glibext_module_ui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H */ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index e62d587..bbe357d 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -33,15 +33,21 @@ #include "buffercache.h" #include "bufferline.h" #include "bufferview.h" -#include "comparison.h" #include "configuration.h" #include "linecursor.h" #include "linegen.h" #include "loadedpanel.h" #include "named.h" -#include "singleton.h" */ +#include "comparable.h" +#include "hashable.h" +#include "objhole.h" #include "portion.h" +#include "secstorage.h" +#include "serialize.h" +#include "singleton.h" +#include "storage.h" +#include "strbuilder.h" #include "work.h" #include "workqueue.h" #include "../helpers.h" @@ -110,12 +116,21 @@ bool populate_glibext_module(void) result = true; + if (result) result = ensure_python_comparable_object_is_registered(); + if (result) result = ensure_python_hashable_object_is_registered(); + if (result) result = ensure_python_serializable_object_is_registered(); + if (result) result = ensure_python_singleton_candidate_is_registered(); + if (result) result = ensure_python_string_builder_is_registered(); + + if (result) result = ensure_python_thick_object_is_registered(); if (result) result = ensure_python_binary_portion_is_registered(); if (result) result = ensure_python_generic_work_is_registered(); + if (result) result = ensure_python_object_storage_is_registered(); + if (result) result = ensure_python_secret_storage_is_registered(); + if (result) result = ensure_python_singleton_factory_is_registered(); if (result) result = ensure_python_work_queue_is_registered(); /* - if (result) result = ensure_python_singleton_candidate_is_registered(); if (result) result = ensure_python_binary_cursor_is_registered(); if (result) result = ensure_python_buffer_cache_is_registered(); @@ -123,7 +138,6 @@ bool populate_glibext_module(void) #ifdef INCLUDE_GTK_SUPPORT if (result) result = ensure_python_buffer_view_is_registered(); #endif - if (result) result = ensure_python_comparable_item_is_registered(); if (result) result = ensure_python_config_param_is_registered(); if (result) result = ensure_python_config_param_iterator_is_registered(); if (result) result = ensure_python_generic_config_is_registered(); @@ -133,7 +147,6 @@ bool populate_glibext_module(void) if (result) result = ensure_python_loaded_panel_is_registered(); if (result) result = ensure_python_named_widget_is_registered(); #endif - if (result) result = ensure_python_singleton_factory_is_registered(); */ assert(result); diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c new file mode 100644 index 0000000..6bea5d1 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.c @@ -0,0 +1,429 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - équivalent Python du fichier "glibext/objhole.c" + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "objhole.h" + + +#include <pygobject.h> + + +#include <i18n.h> +#include <glibext/objhole-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_thick_object_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + +/* Pose un verrou à l'aide du bit dédié de GObject. */ +static PyObject *py_thick_object_lock(PyObject *, PyObject *); + +/* Retire un verrou via le bit dédié de GObject. */ +static PyObject *py_thick_object_unlock(PyObject *, PyObject *); + +/* Indique le nombre de bits accaparés par la GLib. */ +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *); + +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +static PyObject *py_thick_object_get_extra(PyObject *, void *); + +/* Définit la valeur courante de la zone de stockage d'un objet. */ +static int py_thick_object_set_extra(PyObject *, PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define THICK_OBJECT_DOC \ + "The ThickObject class is an alternative version of the GObject class." \ + " It provides an access to a 32-bit wide memory hole inside the native" \ + " GObject structure in order to exploit the area as various values" \ + " storage.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " ThickObject()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Pose un verrou à l'aide du bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Lock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_lock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Retire un verrou via le bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Unlock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_unlock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nombre de bits accaparés par la GLib. * +* * +* Retour : Nombre de bits, à priori inférieur à 32. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + +#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \ + "Quantity of lower bits used by the GLib inside the memory hole.\n" \ + "\n" \ + "The returned value should be less then 32 and is provided for" \ + " pychrysalide.glibext.ThickObject subclass implementation." \ +) + + result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get_extra(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + guint extra; /* Valeur à transmettre */ + +#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + extra, py_thick_object, \ + "Data stored inside the structure memory hole of all GObject" \ + " native instances.\n" \ + "\n" \ + "Less than 32 bits are available for storing arbitrary values." \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + extra = g_thick_object_get_extra(obj); + + result = PyLong_FromUnsignedLong(extra); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* value = valeur fournie à intégrer ou prendre en compte. * +* closure = non utilisé ici. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure) +{ + unsigned long extra; /* Valeur à inscrire */ + GThickObject *obj; /* Version GLib de l'instance */ + + if (!PyLong_Check(value)) + { + PyErr_SetString(PyExc_TypeError, _("Invalid integer value")); + return -1; + } + + extra = PyLong_AsUnsignedLong(value); + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_set_extra(obj, extra); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_thick_object_type(void) +{ + static PyMethodDef py_thick_object_methods[] = { + THICK_OBJECT_LOCK_METHOD, + THICK_OBJECT_UNLOCK_METHOD, + { NULL } + }; + + static PyGetSetDef py_thick_object_getseters[] = { + THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB, + THICK_OBJECT_EXTRA_ATTRIB, + { NULL } + }; + + static PyTypeObject py_thick_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ThickObject", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = THICK_OBJECT_DOC, + + .tp_methods = py_thick_object_methods, + .tp_getset = py_thick_object_getseters, + + .tp_init = py_thick_object_init, + .tp_new = py_thick_object_new, + + }; + + return &py_thick_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_thick_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ThickObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_thick_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en objet dense. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_thick_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to thick object"); + break; + + case 1: + *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/objhole.h b/plugins/pychrysalide/glibext/objhole.h new file mode 100644 index 0000000..b541b70 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - prototypes pour l'équivalent Python du fichier "glibext/objhole.h" + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_thick_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. */ +bool ensure_python_thick_object_is_registered(void); + +/* Tente de convertir en objet dense. */ +int convert_to_thick_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H */ diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c new file mode 100644 index 0000000..5935d29 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.c @@ -0,0 +1,625 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - équivalent Python du fichier "glibext/secstorage.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/secstorage-int.h> + + +#include "../access.h" +#include "../convert.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_secret_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Définit un mot de passe pour protéger une clef maître. */ +static PyObject *py_secret_storage_set_password(PyObject *, PyObject *); + +/* Déverrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_unlock(PyObject *, PyObject *); + +/* Verrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_lock(PyObject *, PyObject *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +static PyObject *py_secret_storage_has_key(PyObject *, void *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +static PyObject *py_secret_storage_is_locked(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GSettings *settings; /* Configuration à considérer */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage natif à manipuler */ + +#define SECRET_STORAGE_DOC \ + "SecretStorage acts as guardian for secrets using ecryption," \ + " mainly for sensitive information stored as configuration" \ + " parameters.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " SecretStorage(settings=None)" \ + "\n" \ + "The *settings* arguement may point to a GSettings instance." \ + " This optional argument is mainly used for testing purpose;" \ + " the main configuration settings are used by default." + + settings = NULL; + + ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings_or_none, &settings); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + if (!g_secret_storage_create(storage, settings)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \ +( \ + set_password, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Create a master key used for protecting secrets. This key is" \ + " itself protected by the provided password.\n" \ + "\n" \ + "The supplied *password* has to be a string.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed,"\ + " *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_set_password(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt in memory the master key used for protecting secrets.\n" \ + "\n" \ + "The supplied *password* is the primary password used to protect" \ + " this key.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed" \ + " or if the master key is already unlocked, *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_unlock(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + +#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "", \ + METH_NOARGS, py_secret_storage, \ + "Clear from memory the master key used for protecting secrets." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + g_secret_storage_lock(storage); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + encrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Encrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either encrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_encrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + decrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either decrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_decrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_has_key(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \ +( \ + key, py_secret_storage, \ + "Indicate if a master key used for protecting secrets seems to have"\ + " been defined. Without any unlocking attempt, the test only relies"\ + " on the length of saved master data.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " seems to exist, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_has_key(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + locked, py_secret_storage, \ + "Indicate if the master key used for protecting secrets is" \ + " currently decrypted in memory.\n" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance; the" \ + " main configuration settings are used by default.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " is unlocked and ready for use, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_is_locked(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_secret_storage_type(void) +{ + static PyMethodDef py_secret_storage_methods[] = { + SECRET_STORAGE_SET_PASSWORD_METHOD, + SECRET_STORAGE_UNLOCK_METHOD, + SECRET_STORAGE_LOCK_METHOD, + SECRET_STORAGE_ENCRYPT_DATA_METHOD, + SECRET_STORAGE_DECRYPT_DATA_METHOD, + { NULL } + }; + + static PyGetSetDef py_secret_storage_getseters[] = { + PY_SECRET_STORAGE_HAS_KEY_ATTRIB, + SECRET_STORAGE_IS_LOCKED_ATTRIB, + { NULL } + }; + + static PyTypeObject py_secret_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SecretStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SECRET_STORAGE_DOC, + + .tp_methods = py_secret_storage_methods, + .tp_getset = py_secret_storage_getseters, + + .tp_init = py_secret_storage_init, + .tp_new = py_secret_storage_new, + + }; + + return &py_secret_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_secret_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'SecretStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_secret_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gardien des secrets avec stockage. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_secret_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage"); + break; + + case 1: + *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h new file mode 100644 index 0000000..68726c3 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_secret_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */ +bool ensure_python_secret_storage_is_registered(void); + +/* Tente de convertir en gardien des secrets avec stockage. */ +int convert_to_secret_storage(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/analysis/storage/serialize.c b/plugins/pychrysalide/glibext/serialize.c index 40fcef7..61f359f 100644 --- a/plugins/pychrysalide/analysis/storage/serialize.c +++ b/plugins/pychrysalide/glibext/serialize.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * serialize.c - équivalent Python du fichier "analysis/storage/serialize.h" + * serialize.c - équivalent Python du fichier "glibext/serialize.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,13 +28,12 @@ #include <pygobject.h> -#include <analysis/storage/serialize-int.h> +#include <glibext/serialize-int.h> #include "storage.h" -#include "../../access.h" -#include "../../helpers.h" -#include "../../common/packed.h" +#include "../access.h" +#include "../helpers.h" @@ -42,23 +41,23 @@ /* Procède à l'initialisation de l'interface de génération. */ -static void py_serializable_object_interface_init(GSerializableObjectIface *, gpointer *); +static void py_serializable_object_interface_init(GSerializableObjectInterface *, gpointer *); -/* Charge un objet depuis une mémoire tampon. */ -static bool py_serializable_object_load_wrapper(GSerializableObject *, GObjectStorage *, packed_buffer_t *); +/* Charge un objet depuis un flux de données. */ +static bool py_serializable_object_load_wrapper(GSerializableObject *, GObjectStorage *, int); -/* Sauvegarde un objet dans une mémoire tampon. */ -static bool py_serializable_object_store_wrapper(const GSerializableObject *, GObjectStorage *, packed_buffer_t *); +/* Sauvegarde un objet dans un flux de données. */ +static bool py_serializable_object_store_wrapper(const GSerializableObject *, GObjectStorage *, int); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Charge un objet depuis une mémoire tampon. */ +/* Charge un objet depuis un flux de données. */ static bool py_serializable_object_load(PyObject *, PyObject *); -/* Sauvegarde un objet dans une mémoire tampon. */ +/* Sauvegarde un objet dans un flux de données. */ static bool py_serializable_object_store(PyObject *, PyObject *); @@ -81,7 +80,7 @@ static bool py_serializable_object_store(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_serializable_object_interface_init(GSerializableObjectIface *iface, gpointer *unused) +static void py_serializable_object_interface_init(GSerializableObjectInterface *iface, gpointer *unused) { #define SERIALIZABLE_OBJECT_DOC \ @@ -94,8 +93,8 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ - "* pychrysalide.analysis.storage.SerializableObject._load();\n" \ - "* pychrysalide.analysis.storage.SerializableObject._store();\n" + "* pychrysalide.glibext.SerializableObject._load();\n" \ + "* pychrysalide.glibext.SerializableObject._store();\n" iface->load = py_serializable_object_load_wrapper; iface->store = py_serializable_object_store_wrapper; @@ -106,10 +105,10 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac /****************************************************************************** * * * Paramètres : object = instruction d'assemblage à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * * * -* Description : Charge un objet depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -117,25 +116,24 @@ static void py_serializable_object_interface_init(GSerializableObjectIface *ifac * * ******************************************************************************/ -static bool py_serializable_object_load_wrapper(GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool py_serializable_object_load_wrapper(GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *storage_obj; /* Objet Python à emmployer */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ #define SERIALIZABLE_OBJECT_LOAD_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _load, "$self, storage, pbuf, /", \ + _load, "$self, storage, fd, /", \ METH_VARARGS, \ - "Abstract method used to load an object definition from buffered data.\n" \ + "Abstract method used to load an object definition from a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) @@ -148,17 +146,9 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb if (has_python_method(pyobj, "_load")) { - if (storage == NULL) - { - storage_obj = Py_None; - Py_INCREF(storage_obj); - } - else - storage_obj = pygobject_new(G_OBJECT(storage)); - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, storage_obj); - PyTuple_SetItem(args, 1, build_from_internal_packed_buffer(pbuf)); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); pyret = run_python_method(pyobj, "_load", args); @@ -182,10 +172,10 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb /****************************************************************************** * * * Paramètres : object = instruction d'assemblage à consulter. * -* storage = conservateur de données à manipuler ou NULL. * -* pbuf = zone tampon à remplir. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * * * -* Description : Sauvegarde un objet dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -193,25 +183,24 @@ static bool py_serializable_object_load_wrapper(GSerializableObject *object, GOb * * ******************************************************************************/ -static bool py_serializable_object_store_wrapper(const GSerializableObject *object, GObjectStorage *storage, packed_buffer_t *pbuf) +static bool py_serializable_object_store_wrapper(const GSerializableObject *object, GObjectStorage *storage, int fd) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *storage_obj; /* Objet Python à emmployer */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ #define SERIALIZABLE_OBJECT_STORE_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _store, "$self, storage, pbuf, /", \ + _store, "$self, storage, fd, /", \ METH_VARARGS, \ - "Abstract method used to store an object definition into buffered data.\n" \ + "Abstract method used to store an object definition into a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) @@ -224,17 +213,9 @@ static bool py_serializable_object_store_wrapper(const GSerializableObject *obje if (has_python_method(pyobj, "_store")) { - if (storage == NULL) - { - storage_obj = Py_None; - Py_INCREF(storage_obj); - } - else - storage_obj = pygobject_new(G_OBJECT(storage)); - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, storage_obj); - PyTuple_SetItem(args, 1, build_from_internal_packed_buffer(pbuf)); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); pyret = run_python_method(pyobj, "_store", args); @@ -266,7 +247,7 @@ static bool py_serializable_object_store_wrapper(const GSerializableObject *obje * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Charge un objet depuis une mémoire tampon. * +* Description : Charge un objet depuis un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -278,32 +259,30 @@ static bool py_serializable_object_load(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ GObjectStorage *storage; /* Conservateur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + int fd; /* Flux ouvert (en lecture) */ int ret; /* Bilan de lecture des args. */ GSerializableObject *object; /* Version native */ bool status; /* Bilan de l'opération */ #define SERIALIZABLE_OBJECT_LOAD_METHOD PYTHON_METHOD_DEF \ ( \ - load, "$self, storage, pbuf, /", \ + load, "$self, storage, fd, /", \ METH_VARARGS, py_serializable_object, \ - "Load an object definition from buffered data.\n" \ + "Load an object definition from a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_object_storage_or_none, &storage, - convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); if (!ret) return NULL; object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); - status = g_serializable_object_load(object, storage, pbuf); + status = g_serializable_object_load(object, storage, fd); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -318,7 +297,7 @@ static bool py_serializable_object_load(PyObject *self, PyObject *args) * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Sauvegarde un objet dans une mémoire tampon. * +* Description : Sauvegarde un objet dans un flux de données. * * * * Retour : Bilan de l'opération. * * * @@ -330,32 +309,30 @@ static bool py_serializable_object_store(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ GObjectStorage *storage; /* Conservateur à manipuler */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + int fd; /* Flux ouvert (en lecture) */ int ret; /* Bilan de lecture des args. */ GSerializableObject *object; /* Version native */ bool status; /* Bilan de l'opération */ #define SERIALIZABLE_OBJECT_STORE_METHOD PYTHON_METHOD_DEF \ ( \ - store, "$self, storage, pbuf, /", \ + store, "$self, storage, fd, /", \ METH_VARARGS, py_serializable_object, \ - "Store an object definition into buffered data.\n" \ + "Store an object definition into a data stream.\n" \ "\n" \ - "The *storage* is a pychrysalide.analysis.storage.ObjectStorage instance" \ - " provided to store inner objects, if relevant, or None. The *pbuf*" \ - " argument points to a pychrysalide.common.PackedBuffer object containing" \ - " the data to process.\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ "\n" \ "The result is a boolean indicating the status of the operation." \ ) - ret = PyArg_ParseTuple(args, "O&O&", convert_to_object_storage_or_none, &storage, - convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); if (!ret) return NULL; object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); - status = g_serializable_object_store(object, storage, pbuf); + status = g_serializable_object_store(object, storage, fd); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -395,7 +372,7 @@ PyTypeObject *get_python_serializable_object_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.analysis.storage.SerializableObject", + .tp_name = "pychrysalide.glibext.SerializableObject", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, @@ -442,7 +419,7 @@ bool ensure_python_serializable_object_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - module = get_access_to_python_module("pychrysalide.analysis.storage"); + module = get_access_to_python_module("pychrysalide.glibext"); dict = PyModule_GetDict(module); diff --git a/plugins/pychrysalide/analysis/storage/serialize.h b/plugins/pychrysalide/glibext/serialize.h index 7e831e5..90688ba 100644 --- a/plugins/pychrysalide/analysis/storage/serialize.h +++ b/plugins/pychrysalide/glibext/serialize.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * serialize.h - prototypes pour l'équivalent Python du fichier "analysis/storage/serialize.h" + * serialize.h - prototypes pour l'équivalent Python du fichier "glibext/serialize.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H -#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_serializable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.analysis.storage.SerializableObject'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.SerializableObject'. */ bool ensure_python_serializable_object_is_registered(void); /* Tente de convertir en objet adapté à une mise en cache. */ @@ -42,4 +42,4 @@ int convert_to_serializable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_SERIALIZE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H */ diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index 8491473..8712506 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -30,7 +30,6 @@ #include <glibext/singleton-int.h> -#include <plugins/dt.h> #include "../access.h" @@ -42,7 +41,7 @@ /* Procède à l'initialisation de l'interface de candidature. */ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *); +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *); /* Fournit une liste de candidats embarqués par un candidat. */ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *); @@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper /* Met à jour une liste de candidats embarqués par un candidat. */ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *); +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *); /* Indique si le candidat est figé. */ -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *); - -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); - -/* Fournit une liste de candidats embarqués par un candidat. */ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *); /* Indique si le candidat est figé. */ -static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *); +static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *); + +/* Crée une copie modifiable d'un object unique. */ +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *); -/* Effectue une comparaison avec un objet 'SingletonCandidate'. */ -static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); +/* Crée une copie modifiable d'un object unique. */ +static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *); @@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused) +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused) { #define SINGLETON_CANDIDATE_DOC \ "The SingletonCandidate class is a required interface for objects" \ " aiming at becoming singleton instances. All shared singletons are" \ " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ + "Implementations of the pychrysalide.glibext.HashableObject and" \ + " pychrysalide.glibext.ComparableObject interfaces are required for" \ + " types implementing the SingletonCandidate interface.\n" \ + "\n" \ "The main implemantations come with types derived from" \ - " pychrysalide.analysis.DataType.\n" \ + " pychrysalide.analysis.DataType (with possible recursivity) or from" \ + " pychrysalide.arch.ArchOperand.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, SingletonCandidate):\n" \ + " class NewImplem(GObject.Object, HashableObject, ComparableObject," \ + " SingletonCandidate):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._dup().\n" \ + "\n" \ + "The following methods may bbe defined for new implementations if" \ + " inner SingletonCandidate objets are carried:\n" \ "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ - "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ - "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \ - "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \ - "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n" + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n" iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; - iface->hash = py_singleton_candidate___hash__wrapper; - iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper; + iface->is_ro = py_singleton_candidate_is_read_only_wrapper; - iface->set_ro = py_singleton_candidate_set_ro_wrapper; - iface->is_ro = py_singleton_candidate_is_ro_wrapper; + iface->dup = py_singleton_candidate_dup_wrapper; } @@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate) +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ - "\n" \ - "Inner instances which are listed through the" \ - " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ - " method do not need to get processed here as they are handled" \ - " automatically by the interface core." \ +#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _mark_as_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to seal the object as unmodifiable.\n" \ + "\n" \ + "No result is expected." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); + pyret = run_python_method(pyobj, "_mark_as_read_only", NULL); - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLongMask(pyret); - - Py_DECREF(pyret); - - } - - } + Py_XDECREF(pyret); Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __eq__, "$self, other, /", \ - METH_NOARGS, \ - "Abstract method used to provide the *__eq__* method for" \ - " rich comparison.\n" \ - "\n" \ - "The expected result is a boolean value." \ +#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the state of the object: are" \ + " its properties frozen (*True*) or can it be modified" \ + " (*False*)?\n" \ + "\n" \ + "The result has to be a boolean status.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a boolean." \ ) - result = 0; + result = false; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__eq__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); - - pyret = run_python_method(pyobj, "__eq__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); + pyret = run_python_method(pyobj, "_is_read_only", NULL); - Py_DECREF(pyret); - - } + if (pyret != NULL) + { + if (PyBool_Check(pyret)) + result = (pyret == Py_True); - Py_DECREF(args); + else + PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value")); } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate) +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate) { + GSingletonCandidate *result; /* Instance à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ + PyObject *state; /* Validation du mode */ -#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \ +#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _set_read_only, "$self, /", \ + _dup, "$self", \ METH_NOARGS, \ - "Abstract method used to mark the content of a singleton" \ - " candidate as read-only.\n" \ + "Abstract method used to create a copy of the object. This" \ + " has to be able to get modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ "\n" \ - "The read-only state is mandatory once the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(candidate)); - - if (has_python_method(pyobj, "_set_read_only")) - { - pyret = run_python_method(pyobj, "_set_read_only", NULL); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _is_read_only, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to retrieve the status of the data" \ - " contained by a singleton candidate.\n" \ + "The result has to be a new intance of type(self).\n" \ + "\n" \ + "A *TypeError* exception is raised if the type of the" \ + " return value is different from the type of self.\n" \ "\n" \ - "The retured value is *True* if the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton, *False* otherwise." \ + "A *ValueError* exception is raised of the return object" \ + " is in read-only mode." \ ) result = false; @@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "_is_read_only")) + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) { - pyret = run_python_method(pyobj, "_is_read_only", NULL); + if (Py_TYPE(pyret) != Py_TYPE(pyobj)) + PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type")); - result = (pyret == Py_True); + else + { + state = py_singleton_candidate_is_read_only(pyret, NULL); - Py_XDECREF(pyret); + if (state != NULL) + { + if (state != Py_False) + PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode")); + + Py_DECREF(state); + + } + + } } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand /****************************************************************************** * * -* Paramètres : self = objet dont l'instance se veut unique. * +* Paramètres : self = objet manipulé ici. * * args = adresse non utilisée ici. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) +static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - guint hash; /* Valeur d'empreitne */ + GSingletonCandidate *copy; /* Copie des mécanismes natifs */ -#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \ -( \ - hash, "$self", \ - METH_NOARGS, py_singleton_candidate, \ - "Compute the hash value of the singleton candidate.\n" \ - "\n" \ - "The method relies on the interface core to include in the" \ - " process the optional embedded instances which may become" \ - " singletons.\n" \ - "\n" \ - "The result is an integer value.\n" \ - "\n" \ - "Even if the Python *hash()* method, relying on the" \ - " pychrysalide.glibext.SingletonCandidate.__hash__()" \ - " implementation, provides values up to 64 bits, the final" \ - " hashes processed by the native GLib hash methods are" \ - " limited to 32 bits values." \ +#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self", \ + METH_NOARGS, py_singleton_candidate, \ + "Create a copy of the object. This has to be able to get" \ + " modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self)." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - hash = g_singleton_candidate_hash(candidate); + copy = g_singleton_candidate_dup(candidate); - result = PyLong_FromUnsignedLong(hash); + if (copy == NULL) + result = NULL; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit une liste de candidats embarqués par un candidat. * -* * -* Retour : Liste de candidats internes, vide si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GSingletonCandidate *candidate; /* Mécanismes natifs */ - size_t count; /* Quantité d'objets internes */ - GSingletonCandidate **instances; /* Liste des embarqués */ - size_t i; /* Boucle de parcours */ - -#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - inner_instances, py_singleton_candidate, \ - "List of optional internal singleton candidate instances.\n" \ - "\n" \ - "The result has to be a tuple containing zero or more" \ - " pychrysalide.glibext.SingletonCandidate instances." \ -) - - candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - - instances = g_singleton_candidate_list_inner_instances(candidate, &count); + else + { + result = pygobject_new(G_OBJECT(candidate)); - result = PyTuple_New(count); + unref_object(copy); - for (i = 0; i < count; i++) - { - PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); - g_object_unref(G_OBJECT(instances[i])); } - if (instances != NULL) - free(instances); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void * * * Description : Indique si le candidat est figé. * * * -* Retour : true si le contenu du candidat ne peut plus être modifié. * +* Retour : True si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure) +static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - bool status; /* Etat de l'élément consulté */ + bool state; /* Etat de l'objet courant */ -#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - read_only, py_singleton_candidate, \ - "State of the singleton candidate content.\n" \ - "\n" \ - "The result is a boolean: *True* if the object is registered" \ - " as singleton, *False* otherwise.\n" \ - "\n" \ - "Once a singleton, the object must not change its content as" \ - " it is a shared instance." \ +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "Boolean state of the object: *True* if all its properties are" \ + " frozen, *False* if the object can be modified." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - status = g_singleton_candidate_is_read_only(candidate); + state = g_singleton_candidate_is_read_only(candidate); - result = status ? Py_True : Py_False; + result = state ? Py_True : Py_False; Py_INCREF(result); - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GSingletonCandidate *cand_a; /* Premier élément à traiter */ - GSingletonCandidate *cand_b; /* Second élément à traiter */ - gboolean status; /* Résultat d'une comparaison */ - - if (op != Py_EQ) - { - result = Py_NotImplemented; - goto cmp_done; - } - - ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a)); - cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b)); - - status = g_singleton_candidate_is_equal(cand_a, cand_b); - - result = (status ? Py_True : Py_False); - - cmp_done: - - Py_INCREF(result); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void) static PyMethodDef py_singleton_candidate_methods[] = { SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, - SINGLETON_CANDIDATE_HASH_WRAPPER, - SINGLETON_CANDIDATE_EQ_WRAPPER, - SINGLETON_CANDIDATE_SET_RO_WRAPPER, - SINGLETON_CANDIDATE_IS_RO_WRAPPER, - SINGLETON_CANDIDATE_HASH_METHOD, + SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_DUP_WRAPPER, + SINGLETON_CANDIDATE_DUP_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { - SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void) .tp_doc = SINGLETON_CANDIDATE_DOC, - .tp_richcompare = py_singleton_candidate_richcompare, - .tp_methods = py_singleton_candidate_methods, .tp_getset = py_singleton_candidate_getseters @@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void) bool ensure_python_singleton_candidate_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinContent' */ + PyTypeObject *type; /* Type 'SingletonCandidate' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst) /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - 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 */ - - /* Validations diverses */ - - base = get_python_singleton_factory_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k "\n" \ "The first processed instance defines the type handled by the factory." - /* Récupération des paramètres */ - - ret = PyArg_ParseTuple(args, ""); - if (!ret) return -1; - /* Initialisation d'un objet GLib */ ret = forward_pygobjet_init(self); diff --git a/plugins/pychrysalide/analysis/storage/storage.c b/plugins/pychrysalide/glibext/storage.c index c54fe0f..f2962bf 100644 --- a/plugins/pychrysalide/analysis/storage/storage.c +++ b/plugins/pychrysalide/glibext/storage.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * storage.c - équivalent Python du fichier "analysis/storage/storage.c" + * storage.c - équivalent Python du fichier "glibext/storage.c" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,25 +25,23 @@ #include "storage.h" +#include <assert.h> #include <pygobject.h> -#include <analysis/storage/storage-int.h> -#include <plugins/dt.h> +#include <glibext/storage-int.h> #include "serialize.h" -#include "../../access.h" -#include "../../helpers.h" -#include "../../common/packed.h" +#include "../access.h" +#include "../helpers.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_object_storage_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(object_storage, G_TYPE_OBJECT_STORAGE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_object_storage_init(PyObject *, PyObject *, PyObject *); @@ -68,9 +66,6 @@ static PyObject *py_object_storage_unpack_object(PyObject *, PyObject *); /* Sauvegarde un object sous forme de données rassemblées. */ static PyObject *py_object_storage_store_object(PyObject *, PyObject *); -/* Sauvegarde un object interne sous forme de données. */ -static PyObject *py_object_storage_pack_object(PyObject *, PyObject *); - /* ---------------------------------------------------------------------------------- */ @@ -80,66 +75,6 @@ static PyObject *py_object_storage_pack_object(PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_object_storage_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - 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 */ - - /* Validations diverses */ - - base = get_python_object_storage_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_OBJECT_STORAGE, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -154,7 +89,9 @@ static PyObject *py_object_storage_new(PyTypeObject *type, PyObject *args, PyObj static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds) { - const char *hash; /* Empreinte de contenu */ + const char *type; /* Type global de conservation */ + unsigned char version; /* Version de ce type */ + const char *uid; /* Identifiant de distinction */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ @@ -164,14 +101,15 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " ObjectStorage(hash)" \ + " ObjectStorage(type, version uid)" \ "\n" \ - "Where *hash* should a string built from the checksum of the" \ - " relative binary content linked to the storage.pychrysalide." + "Where *type* is a short string describing the storage kind," \ + " *version* provides a version control for this type and *uid* is" \ + " an arbitrary unique identifier used for creating temporary files." /* Récupération des paramètres */ - ret = PyArg_ParseTuple(args, "s", &hash); + ret = PyArg_ParseTuple(args, "sbs", &type, &version, &uid); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -183,7 +121,8 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds storage = G_OBJECT_STORAGE(pygobject_get(self)); - storage->hash = strdup(hash); + if (!g_object_storage_create(storage, type, version, uid)) + return -1; return 0; @@ -212,27 +151,27 @@ static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds static PyObject *py_object_storage_load(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *filename; /* Fichier de source à traiter */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ -#define OBJECT_STORAGE_LOAD_METHOD PYTHON_METHOD_DEF \ -( \ - load, "pbuf, /", \ - METH_STATIC | METH_VARARGS, py_object_storage, \ - "Construct a new storage from a buffer.\n" \ - "\n" \ - "The *pbuf* has to be an instance of type" \ - " pychrysalide.common.PackedBuffer.\n" \ - "\n" \ - "The result is a new pychrysalide.analysis.storage.ObjectStorage" \ - " object on success, *None* otherwise." \ +#define OBJECT_STORAGE_LOAD_METHOD PYTHON_METHOD_DEF \ +( \ + load, "filename, /", \ + METH_STATIC | METH_VARARGS, py_object_storage, \ + "Construct a new storage from a filename.\n" \ + "\n" \ + "The *filename* argument points to the source file to" \ + " read.\n" \ + "\n" \ + "The result is a new pychrysalide.glibext.ObjectStorage" \ + " object on success, *None* otherwise." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "s", &filename); if (!ret) return NULL; - storage = g_object_storage_load(pbuf); + storage = g_object_storage_load(filename); if (storage == NULL) { @@ -242,7 +181,7 @@ static PyObject *py_object_storage_load(PyObject *self, PyObject *args) else { result = pygobject_new(G_OBJECT(storage)); - g_object_unref(G_OBJECT(storage)); + unref_object(storage); } return result; @@ -266,29 +205,29 @@ static PyObject *py_object_storage_load(PyObject *self, PyObject *args) static PyObject *py_object_storage_store(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ + const char *filename; /* Fichier de destination */ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ bool status; /* Bilan de l'opération */ #define OBJECT_STORAGE_STORE_METHOD PYTHON_METHOD_DEF \ ( \ - store, "$self, pbuf, /", \ + store, "$self, filename, /", \ METH_VARARGS, py_object_storage, \ - "Save a storage into a buffer.\n" \ + "Save a storage into a file.\n" \ "\n" \ - "The *pbuf* has to be an instance of type" \ - " pychrysalide.common.PackedBuffer.\n" \ + "The *filename* argument points to the destination" \ + " file to write.\n" \ "\n" \ "The result is *True* on success, *False* otherwise." \ ) - ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "s", &filename); if (!ret) return NULL; storage = G_OBJECT_STORAGE(pygobject_get(self)); - status = g_object_storage_store(storage, pbuf); + status = g_object_storage_store(storage, filename); result = status ? Py_True : Py_False; Py_INCREF(result); @@ -331,7 +270,7 @@ static PyObject *py_object_storage_load_object(PyObject *self, PyObject *args) " the data to unserialize.\n" \ "\n" \ "The result is a pychrysalide.analysis.storage.SerializableObject" \ - " instancet in case of success, or None in case of failure." \ + " instancet in case of success, or *None* in case of failure." \ ) ret = PyArg_ParseTuple(args, "sK", &name, &pos); @@ -370,31 +309,34 @@ static PyObject *py_object_storage_load_object(PyObject *self, PyObject *args) static PyObject *py_object_storage_unpack_object(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ + int fd; /* Flux de fonnées courant */ const char *name; /* Désignation de groupe */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ int ret; /* Bilan de lecture des args. */ GObjectStorage *storage; /* Mécanismes natifs */ GSerializableObject *object; /* Objet reconstruit ou NULL */ #define OBJECT_STORAGE_UNPACK_OBJECT_METHOD PYTHON_METHOD_DEF \ ( \ - unpack_object, "$self, name, pbuf, /", \ + unpack_object, "$self, fd, name, /", \ METH_VARARGS, py_object_storage, \ - "Load an object from a buffer with a location pointing to data.\n" \ + "Load an object from a reference to serialized data.\n" \ "\n" \ - "The *name* is a string label for the group of target objects and" \ - " *pbuf* has to be a pychrysalide.common.PackedBuffer instance.\n" \ + "The *fd* argument is a file descriptor pointing to the data" \ + " stream for a current object being restored. A reference to" \ + " another object belonging to a group pointed by the string *name*" \ + " should be available at the current read position for this data" \ + " stream.\n" \ "\n" \ "The result is a pychrysalide.analysis.storage.SerializableObject" \ - " instancet in case of success, or None in case of failure." \ + " instancet in case of success, or *None* in case of failure." \ ) - ret = PyArg_ParseTuple(args, "sO&", &name, convert_to_packed_buffer, &pbuf); + ret = PyArg_ParseTuple(args, "is", &fd, &name); if (!ret) return NULL; storage = G_OBJECT_STORAGE(pygobject_get(self)); - object = g_object_storage_unpack_object(storage, name, pbuf); + object = g_object_storage_unpack_object(storage, fd, name); if (object != NULL) result = pygobject_new(G_OBJECT(object)); @@ -443,7 +385,7 @@ static PyObject *py_object_storage_store_object(PyObject *self, PyObject *args) " pychrysalide.analysis.storage.SerializableObject instance.\n" \ "\n" \ "The result is the position of the data for stored object," \ - " provided as an integer offset, in case of success or None" \ + " provided as an integer offset, in case of success or *None*" \ " in case of failure." \ ) @@ -469,62 +411,6 @@ static PyObject *py_object_storage_store_object(PyObject *self, PyObject *args) /****************************************************************************** * * -* Paramètres : self = classe représentant une mémorisation de types. * -* args = arguments fournis à l'appel. * -* * -* Description : Sauvegarde un object interne sous forme de données. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_object_storage_pack_object(PyObject *self, PyObject *args) -{ - PyObject *result; /* Emplacement à retourner */ - const char *name; /* Désignation de groupe */ - GSerializableObject *object; /* Objet à traiter */ - packed_buffer_t *pbuf; /* Tampon de données à employer*/ - int ret; /* Bilan de lecture des args. */ - GObjectStorage *storage; /* Mécanismes natifs */ - bool status; /* Bilan de l'opération */ - -#define OBJECT_STORAGE_PACK_OBJECT_METHOD PYTHON_METHOD_DEF \ -( \ - pack_object, "$self, name, object, pbuf/", \ - METH_VARARGS, py_object_storage, \ - "Save an object as serialized data and store the location of" \ - " the data intro a buffer.\n" \ - "\n" \ - "The *name* is a string label for the group of target objects," \ - " the processed *object* has to be a" \ - " pychrysalide.analysis.storage.SerializableObject instance" \ - " and *pbuf* is expected to be a" \ - " pychrysalide.common.PackedBuffer instance.\n" \ - "\n" \ - "The status of the operation is returned as a boolean value:" \ - " *True* for success, *False* for failure." \ -) - - ret = PyArg_ParseTuple(args, "sO&O&", &name, convert_to_serializable_object, &object, - convert_to_packed_buffer, &pbuf); - if (!ret) return NULL; - - storage = G_OBJECT_STORAGE(pygobject_get(self)); - - status = g_object_storage_pack_object(storage, name, object, pbuf); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : - * * * * Description : Fournit un accès à une définition de type à diffuser. * @@ -543,7 +429,6 @@ PyTypeObject *get_python_object_storage_type(void) OBJECT_STORAGE_LOAD_OBJECT_METHOD, OBJECT_STORAGE_UNPACK_OBJECT_METHOD, OBJECT_STORAGE_STORE_OBJECT_METHOD, - OBJECT_STORAGE_PACK_OBJECT_METHOD, { NULL } }; @@ -555,7 +440,7 @@ PyTypeObject *get_python_object_storage_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.analysis.storage.ObjectStorage", + .tp_name = "pychrysalide.glibext.ObjectStorage", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT, @@ -579,7 +464,7 @@ PyTypeObject *get_python_object_storage_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide....ObjectStorage'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'.* * * * Retour : Bilan de l'opération. * * * @@ -597,7 +482,7 @@ bool ensure_python_object_storage_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - module = get_access_to_python_module("pychrysalide.analysis.storage"); + module = get_access_to_python_module("pychrysalide.glibext"); dict = PyModule_GetDict(module); diff --git a/plugins/pychrysalide/analysis/storage/storage.h b/plugins/pychrysalide/glibext/storage.h index a0a2c18..681f99a 100644 --- a/plugins/pychrysalide/analysis/storage/storage.h +++ b/plugins/pychrysalide/glibext/storage.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * storage.h - prototypes pour l'équivalent Python du fichier "analysis/storage/storage.h" + * storage.h - prototypes pour l'équivalent Python du fichier "glibext/storage.h" * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H -#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_object_storage_type(void); -/* Prend en charge l'objet 'pychrysalide.analysis.storage.ObjectStorage'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'. */ bool ensure_python_object_storage_is_registered(void); /* Tente de convertir en conservateur d'objets. */ @@ -45,4 +45,4 @@ int convert_to_object_storage_or_none(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_STORAGE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H */ diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c new file mode 100644 index 0000000..a6de0f0 --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.c @@ -0,0 +1,542 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "strbuilder.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/strbuilder-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet la description d'un objet définie par son parent. */ +static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static PyObject *py_string_builder_to_string(PyObject *, PyObject *); + +/* Fournit une représentation de l'objet exportable. */ +static PyObject *py_string_builder_str(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused) +{ +#define STRING_BUILDER_DOC \ + "The StringBuilder class defines a interface for native objects aiming" \ + " at providing a string representation of themselves.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, StringBuilder):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.StringBuilder._to_string().\n" + + iface->to_string = py_string_builder_to_string_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + const char *utf8; /* Chaîne UTF-8 portée */ + Py_ssize_t size; /* Taille de ces données */ + +#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _to_string, "$self, /, flags=0", \ + METH_VARARGS, \ + "Abstract method providing a string representation for the" \ + " object which is used as the default implementation of the" \ + " __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result has to be a string." \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a string." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(builder)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); + + pyret = run_python_method(pyobj, "_to_string", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + { + utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); + + if (utf8 != NULL) + { + assert(size >= 0); + + add_to_sized_binary(out, utf8, size); + result = true; + + } + + } + + if (!result) + PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet la description d'un objet définie par son parent. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + GStringBuilderInterface *iface; /* Interface utilisée */ + GStringBuilderInterface *parent_iface; /* Interface parente */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + parent_to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.StringBuilder" \ + " interface." \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._to_string() function has to be" \ + " used instead.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface")); + + result = NULL; + + } + else + { + init_sized_binary(&out); + + status = parent_iface->to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation for the object which is used" \ + " as the default implementation of the __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Fournit une représentation de l'objet exportable. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_str(PyObject *self) +{ + PyObject *result; /* Emplacement à retourner */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, 0, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_string_builder_type(void) +{ + static PyMethodDef py_string_builder_methods[] = { + STRING_BUILDER_TO_STRING_WRAPPER, + STRING_BUILDER_PARENT_TO_STRING_METHOD, + STRING_BUILDER_TO_STRING_METHOD, + { NULL } + }; + + static PyGetSetDef py_string_builder_getseters[] = { + { NULL } + }; + + static PyTypeObject py_string_builder_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.StringBuilder", + .tp_basicsize = sizeof(PyObject), + + .tp_str = py_string_builder_str, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = STRING_BUILDER_DOC, + + .tp_methods = py_string_builder_methods, + .tp_getset = py_string_builder_getseters + + }; + + return &py_string_builder_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_string_builder_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'StringBuilder' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_string_builder_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'exportation en chaîne. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_string_builder(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to string builder"); + break; + + case 1: + *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h new file mode 100644 index 0000000..1881cae --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_string_builder_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */ +bool ensure_python_string_builder_is_registered(void); + +/* Tente de convertir en interface d'exportation en chaîne. */ +int convert_to_string_builder(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */ diff --git a/plugins/pychrysalide/analysis/storage/tpmem.c b/plugins/pychrysalide/glibext/tpmem.c index ae07008..ae07008 100644 --- a/plugins/pychrysalide/analysis/storage/tpmem.c +++ b/plugins/pychrysalide/glibext/tpmem.c diff --git a/plugins/pychrysalide/analysis/storage/tpmem.h b/plugins/pychrysalide/glibext/tpmem.h index 1085632..1085632 100644 --- a/plugins/pychrysalide/analysis/storage/tpmem.h +++ b/plugins/pychrysalide/glibext/tpmem.h diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c index 6a15984..e6791e3 100644 --- a/plugins/pychrysalide/glibext/work.c +++ b/plugins/pychrysalide/glibext/work.c @@ -41,9 +41,9 @@ /* Initialise la classe des travaux programmés. */ -static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer); +static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_generic_work_init(PyObject *, PyObject *, PyObject *); @@ -68,20 +68,22 @@ static PyObject *py_generic_work_process(PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des travaux programmés. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused) +static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass) { - class->run = py_generic_work_run_wrapper; + PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper); + + return 0; } @@ -296,6 +298,8 @@ bool ensure_python_generic_work_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type)) return false; diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c index d8126be..ca6c73c 100644 --- a/plugins/pychrysalide/glibext/workqueue.c +++ b/plugins/pychrysalide/glibext/workqueue.c @@ -94,9 +94,9 @@ static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds) { int ret; /* Bilan de lecture des args. */ -#define WORK_QUEUE_DOC \ - "WorkQueue defines a basic work aimed to get processed in a" \ - " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \ +#define WORK_QUEUE_DOC \ + "WorkQueue creates threads in order to process" \ + " pychrysalide.glibext.Work instances.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ diff --git a/plugins/pychrysalide/gtkext/Makefile.am b/plugins/pychrysalide/gtkext/Makefile.am index 2e1260f..1d91751 100644 --- a/plugins/pychrysalide/gtkext/Makefile.am +++ b/plugins/pychrysalide/gtkext/Makefile.am @@ -1,19 +1,23 @@ noinst_LTLIBRARIES = libpychrysagtkext.la +# libpychrysagtkext_la_SOURCES = \ +# blockdisplay.h blockdisplay.c \ +# bufferdisplay.h bufferdisplay.c \ +# displaypanel.h displaypanel.c \ +# dockable.h dockable.c \ +# easygtk.h easygtk.c \ +# module.h module.c \ +# named.h named.c + libpychrysagtkext_la_SOURCES = \ - blockdisplay.h blockdisplay.c \ - bufferdisplay.h bufferdisplay.c \ - displaypanel.h displaypanel.c \ - dockable.h dockable.c \ - easygtk.h easygtk.c \ - module.h module.c \ - named.h named.c - -libpychrysagtkext_la_LIBADD = \ - graph/libpychrysagtkextgraph.la - -libpychrysagtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + panel.h panel.c \ + module.h module.c + +# libpychrysagtkext_la_LIBADD = \ +# graph/libpychrysagtkextgraph.la + +libpychrysagtkext_la_CFLAGS = $(LIBGTK4_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT @@ -22,4 +26,4 @@ devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(libpychrysagtkext_la_SOURCES:%c=) -SUBDIRS = graph +#SUBDIRS = graph diff --git a/plugins/pychrysalide/gtkext/module.c b/plugins/pychrysalide/gtkext/module.c index f8264af..fa59b2b 100644 --- a/plugins/pychrysalide/gtkext/module.c +++ b/plugins/pychrysalide/gtkext/module.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * module.c - intégration du répertoire gtkext en tant que module * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,13 +28,13 @@ #include <assert.h> -#include "blockdisplay.h" -#include "bufferdisplay.h" -#include "displaypanel.h" -#include "dockable.h" -#include "easygtk.h" -#include "named.h" -#include "graph/module.h" +//#include "blockdisplay.h" +//#include "bufferdisplay.h" +//#include "displaypanel.h" +//#include "dockable.h" +//#include "named.h" +#include "panel.h" +//#include "graph/module.h" #include "../helpers.h" @@ -71,7 +71,7 @@ bool add_gtkext_module(PyObject *super) result = (module != NULL); - if (result) result = add_gtkext_graph_module(module); + //if (result) result = add_gtkext_graph_module(module); if (!result) Py_XDECREF(module); @@ -99,14 +99,16 @@ bool populate_gtkext_module(void) result = true; - if (result) result = ensure_python_block_display_is_registered(); - if (result) result = ensure_python_buffer_display_is_registered(); - if (result) result = ensure_python_display_panel_is_registered(); - if (result) result = ensure_python_dockable_is_registered(); - if (result) result = ensure_python_easygtk_is_registered(); - if (result) result = ensure_python_built_named_widget_is_registered(); + if (result) result = ensure_python_tiled_panel_is_registered(); - if (result) result = populate_gtkext_graph_module(); + //if (result) result = ensure_python_block_display_is_registered(); + //if (result) result = ensure_python_buffer_display_is_registered(); + //if (result) result = ensure_python_display_panel_is_registered(); + //if (result) result = ensure_python_dockable_is_registered(); + //if (result) result = ensure_python_easygtk_is_registered(); + //if (result) result = ensure_python_built_named_widget_is_registered(); + + //if (result) result = populate_gtkext_graph_module(); assert(result); diff --git a/plugins/pychrysalide/gtkext/panel.c b/plugins/pychrysalide/gtkext/panel.c new file mode 100644 index 0000000..9f6589a --- /dev/null +++ b/plugins/pychrysalide/gtkext/panel.c @@ -0,0 +1,130 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.c - prototypes pour l'équivalent Python du fichier "gtkext/panel.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "panel.h" + + +#include <pygobject.h> + + +#include <gtkext/panel.h> + + +#include "../access.h" +#include "../helpers.h" +#include "../helpers-ui.h" + + + +#define TILED_PANEL_DOC \ + "The TiledPanel class defines a panel widget for the framework main" \ + " window." + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_tiled_panel_type(void) +{ + static PyMethodDef py_tiled_panel_methods[] = { + { NULL } + }; + + static PyGetSetDef py_tiled_panel_getseters[] = { + { NULL } + }; + + static PyTypeObject py_tiled_panel_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.gtkext.TiledPanel", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = TILED_PANEL_DOC, + + .tp_methods = py_tiled_panel_methods, + .tp_getset = py_tiled_panel_getseters, + + }; + + static PyTypeObject *result = NULL; + + if (result == NULL) + result = define_python_dynamic_type(&py_tiled_panel_type); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.gtkext.TiledPanel'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_tiled_panel_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'TiledPanel' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_tiled_panel_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.gtkext"); + + dict = PyModule_GetDict(module); + + if (!ensure_gtk_widget_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, GTK_TYPE_TILED_PANEL, type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/gtkext/panel.h b/plugins/pychrysalide/gtkext/panel.h new file mode 100644 index 0000000..c5cbe86 --- /dev/null +++ b/plugins/pychrysalide/gtkext/panel.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.h - prototypes pour l'équivalent Python du fichier "gtkext/panel.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H +#define _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_tiled_panel_type(void); + +/* Prend en charge l'objet 'pychrysalide.gtkext.TiledPanel'. */ +bool ensure_python_tiled_panel_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GTKEXT_PANEL_H */ diff --git a/plugins/pychrysalide/helpers-ui.c b/plugins/pychrysalide/helpers-ui.c new file mode 100644 index 0000000..982e676 --- /dev/null +++ b/plugins/pychrysalide/helpers-ui.c @@ -0,0 +1,141 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers-ui.c - simplification des interactions UI de base avec Python + * + * Copyright (C) 2025 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 "helpers-ui.h" + + +#include <assert.h> +#include <pygobject.h> +#include <gtk/gtk.h> + + +#include "bindings.h" + + + +/* ---------------------------------------------------------------------------------- */ +/* CONFORTS CIBLANT PYGOBJECT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure une prise en charge de l'objet Gtk.WIdget. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_gtk_widget_is_registered(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gtk_mod; /* Module Python Gtk */ + PyObject *widget_type; /* Module "GtkWidget" */ + + /** + * Afin d'éviter le message d'avertissement suivant, la version attendue + * est demandée : + * + * PyGIWarning: Gtk was imported without specifying a version first. + * Use gi.require_version('Gtk', '4.0') before import to ensure that the right version gets loaded. + * + */ + result = import_namespace_from_gi_repository("Gtk", "4.0"); + + if (result) + { + gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); + assert(gtk_mod != NULL); + + widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); + + result = (widget_type != NULL); + + Py_DECREF(gtk_mod); + Py_XDECREF(widget_type); + + } + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en instance de composant GTK. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gtk_widget(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + PyObject *gtk_mod; /* Module Python Gtk */ + PyObject *widget_type; /* Module "GtkWidget" */ + int ret; /* Bilan d'une conversion */ + + result = 0; + + gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); + + if (gtk_mod == NULL) + { + PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); + goto done; + } + + widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); + + Py_DECREF(gtk_mod); + + ret = PyObject_TypeCheck(arg, (PyTypeObject *)widget_type); + + Py_DECREF(widget_type); + + if (!ret) + { + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK widget"); + goto done; + } + + *((GtkWidget **)dst) = GTK_WIDGET(pygobject_get(arg)); + + result = 1; + + done: + + return result; + +} diff --git a/plugins/pychrysalide/helpers-ui.h b/plugins/pychrysalide/helpers-ui.h new file mode 100644 index 0000000..b575905 --- /dev/null +++ b/plugins/pychrysalide/helpers-ui.h @@ -0,0 +1,44 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers-ui.h - prototypes pour la simplification des interactions UI de base avec Python + * + * Copyright (C) 2025 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_HELPERS_UI_H +#define _PLUGINS_PYCHRYSALIDE_HELPERS_UI_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* --------------------------- CONFORTS CIBLANT PYGOBJECT --------------------------- */ + + +/* Assure une prise en charge de l'objet Gtk.WIdget. */ +bool ensure_gtk_widget_is_registered(void); + +/* Tente de convertir en instance de composant GTK. */ +int convert_to_gtk_widget(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_HELPERS_UI_H */ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index d361c3e..4ff768c 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.c - simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -32,14 +32,10 @@ #include <stdlib.h> #include <string.h> #include <strings.h> -#ifdef INCLUDE_GTK_SUPPORT -# include <gtk/gtk.h> -#endif #include <i18n.h> #include <common/extstr.h> -#include <plugins/dt.h> #include "access.h" @@ -217,6 +213,53 @@ bool has_python_method(PyObject *module, const char *method) * * * Paramètres : target = propriétaire de la routine visée. * * method = désignation de la fonction à appeler. * +* * +* Description : Indique si une routine Python possède une implémentation. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool has_python_implementation_method(PyObject *module, const char *method) +{ + bool result; /* Bilan à retourner */ + PyObject *func; /* Fonction visée */ + const PyMethodDef *def; /* Définition de la fonction */ + + result = (PyObject_HasAttrString(module, method) == 1); + + if (result) + { + func = PyObject_GetAttrString(module, method); + assert(func != NULL); + + result = PyCallable_Check(func); + + if (func->ob_type == &PyCFunction_Type) + { + def = ((PyCFunctionObject *)func)->m_ml; + + assert(strcmp(def->ml_name, method) == 0); + + result = (def->ml_meth != not_yet_implemented_method); + + } + + Py_DECREF(func); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = propriétaire de la routine visée. * +* method = désignation de la fonction à appeler. * * args = arguments à associer à l'opération. * * * * Description : Appelle une routine Python. * @@ -517,75 +560,10 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) * * ******************************************************************************/ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = pygobject_lookup_class(gbase); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(gbase, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type du nouvel objet à mettre en place. * -* gbase = type de base natif. * -* cinit = procédure d'initialisation de la classe associée. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition.* -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds) +PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ /* Validations diverses */ @@ -598,24 +576,6 @@ PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GTy goto exit; } - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - 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); @@ -1159,6 +1119,60 @@ int forward_pygobjet_init(PyObject *self) /****************************************************************************** * * +* Paramètres : type = type Python à ausculter. * +* * +* Description : Détermine si un type Python est implémenté en C ou non. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool pytype_has_native_implementation(const PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type associé à la classe */ + + static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */ + + /** + * Dans les sources de PyGObject, la fonction pyg_type_register() de + * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code + * suivante : + * + * // Mark this GType as a custom python type + * g_type_set_qdata(instance_type, pygobject_custom_key, + * GINT_TO_POINTER (1)); + * + * La fonction pyi_object_register_types() de gi/pygobject-object.c indique + * la clef associée au Quark : + * + * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + * + * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c : + * + * gboolean pyg_gtype_is_custom(GType gtype) + * { + * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL; + * } + * + */ + + if (pygobject_custom_key == 0) + pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + + gtype = pyg_type_from_object((PyObject *)type); + + result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * @@ -1243,122 +1257,6 @@ int convert_to_gobject(PyObject *arg, void *dst) #if 0 -#ifdef INCLUDE_GTK_SUPPORT - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en instance de composant GTK. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_gtk_widget(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - PyObject *gtk_mod; /* Module Python Gtk */ - PyObject *widget_type; /* Module "GtkWidget" */ - int ret; /* Bilan d'une conversion */ - - result = 0; - - gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); - - if (gtk_mod == NULL) - { - PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); - goto done; - } - - widget_type = PyObject_GetAttrString(gtk_mod, "Widget"); - - Py_DECREF(gtk_mod); - - ret = PyObject_TypeCheck(arg, (PyTypeObject *)widget_type); - - Py_DECREF(widget_type); - - if (!ret) - { - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK widget"); - goto done; - } - - *((GtkWidget **)dst) = GTK_WIDGET(pygobject_get(arg)); - - result = 1; - - done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en instance de conteneur GTK. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_gtk_container(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - PyObject *gtk_mod; /* Module Python Gtk */ - PyObject *container_type; /* Module "GtkContainer" */ - int ret; /* Bilan d'une conversion */ - - result = 0; - - gtk_mod = PyImport_ImportModule("gi.repository.Gtk"); - - if (gtk_mod == NULL) - { - PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module"); - goto done; - } - - container_type = PyObject_GetAttrString(gtk_mod, "Container"); - - Py_DECREF(gtk_mod); - - ret = PyObject_TypeCheck(arg, (PyTypeObject *)container_type); - - Py_DECREF(container_type); - - if (!ret) - { - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK container"); - goto done; - } - - *((GtkContainer **)dst) = GTK_CONTAINER(pygobject_get(arg)); - - result = 1; - - done: - - return result; - -} - - -#endif - - /****************************************************************************** * * * Paramètres : color = couleur dans sa définition native à copier. * @@ -1486,6 +1384,109 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst) +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en tableau de chaînes de caractères. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_sequence_to_charp_array(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + charp_array_t *array; /* Tableau à constituer */ + size_t i; /* Boucle de parcours */ + PyObject *value; /* Valeur brute d'un élément */ + + array = (charp_array_t *)dst; + + /* Nettoyage ? */ + if (arg == NULL) + { + result = 1; + goto clean; + } + + else + { + result = 0; + + if (PySequence_Check(arg) != 1) + goto done; + + array->length = PySequence_Length(arg); + + array->values = calloc(array->length, sizeof(char *)); + + for (i = 0; i < array->length; i++) + { + value = PySequence_ITEM(arg, i); + + if (!PyUnicode_Check(value)) + { + Py_DECREF(value); + goto clean; + } + + array->values[i] = strdup(PyUnicode_DATA(value)); + + Py_DECREF(value); + + } + + result = Py_CLEANUP_SUPPORTED; + + } + + done: + + return result; + + clean: + + clean_charp_array(array); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau de chaînes de caractères à traiter. * +* * +* Description : Libère de la mémoire un tableau de chaînes de caractères. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void clean_charp_array(charp_array_t *array) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < array->length; i++) + if (array->values[i] != NULL) + free(array->values[i]); + + if (array->values != NULL) + free(array->values); + + array->values = NULL; + array->length = 0; + +} + + + /* ---------------------------------------------------------------------------------- */ /* TRANSFERT DES VALEURS CONSTANTES */ /* ---------------------------------------------------------------------------------- */ diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index cc82bba..f1c6337 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.h - prototypes pour la simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,6 +34,9 @@ #endif +#include <i18n.h> + + /* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */ @@ -47,6 +50,9 @@ int convert_to_callable(PyObject *, void *); /* Indique si une routine Python existe ou non. */ bool has_python_method(PyObject *, const char *); +/* Indique si une routine Python possède une implémentation. */ +bool has_python_implementation_method(PyObject *, const char *); + /* Appelle une routine Python. */ PyObject *run_python_method(PyObject *, const char *, PyObject *); @@ -126,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *); PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_HAS_DEF_FULL(name, base, doc) \ - PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) + PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_RAWGET_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL) @@ -152,14 +158,11 @@ bool register_python_module_object(PyObject *, PyTypeObject *); * * Cf. http://stackoverflow.com/questions/20432335/can-python-abstract-base-classes-inherit-from-c-extensions */ -#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new +#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new // REMME /* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, GType, PyObject *, PyObject *); - -/* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *); +PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *); #define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \ @@ -167,22 +170,41 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_constructor_with_dynamic_gtype(type, gbase, args, kwds); \ + result = PyType_GenericNew(type, args, kwds); \ return result; \ } -#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \ +#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase) \ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); \ static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_abstract_constructor_with_dynamic_gtype(type, gbase, (GClassInitFunc)cinit, \ - args, kwds); \ + result = python_abstract_constructor(type, gbase, args, kwds); \ return result; \ } +/** + * Les initialisations de classes engagées par les appels à pyg_register_class_init() + * ne se déclenchent qu'après les initialisations complètes des classes côté GObject. + * + * Typiquement, pour une déclinaison Python du type PythonModule, sont appelées + * successivement les fonctions suivantes : + * - g_plugin_module_class_init() ; + * - g_python_plugin_class_init() ; + * - py_plugin_module_init_gclass(). + * + * Il est alors impératif de considérer les pointeurs de fonction déjà en place + * afin de ne par remplacer les implémentations de GPythonPlugin par les + * wrappers par défaut de PythonModule. + */ + +#define PY_CLASS_SET_WRAPPER(field, ptr) \ + if (field == NULL) \ + field = ptr; + + /* Marque l'interdiction d'une instanciation depuis Python. */ PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *); @@ -206,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *); /** + * Prise en compte d'éventuelles exceptions levées dans les implémentations. + * + * Par exemple : + * - du code Python exécute une fonction implémentée en C ; + * - cette dernière fait appel à un Wrapper C qui sollicite du code + * d'implémentation Python. + * + * Cette seconde étape peut lever une exception (impletation manquante ou + * implémentation levant une exception. + * + * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre + * le détail des éventuelles exceptions, mais Python le mémorise. + */ + +#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + result = val; \ + } \ + while (0) + +#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + { \ + Py_XDECREF(result); \ + result = NULL; \ + } \ + } \ + while (0) + + +/** * pygobject_new() prend en compte les références flottantes au moment de la * construction d'un objet Python. * @@ -231,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *); bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *); /* Enregistre un type Python dérivant d'un type GLib dynamique. */ -bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); +bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME /* Fait suivre à la partie GObject une initialisation nouvelle. */ int forward_pygobjet_init(PyObject *); +/* Détermine si un type Python est implémenté en C ou non. */ +bool pytype_has_native_implementation(const PyTypeObject *); + +#define check_for_native_parent(obj) \ + ({ \ + bool __result; \ + __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \ + if (!__result) \ + PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \ + __result; \ + }) + /* Tente de convertir en valeur GType. */ int convert_to_gtype(PyObject *, void *); @@ -244,17 +313,6 @@ int convert_to_gobject(PyObject *, void *); #if 0 -#ifdef INCLUDE_GTK_SUPPORT - -/* Tente de convertir en instance de composant GTK. */ -int convert_to_gtk_widget(PyObject *, void *); - -/* Tente de convertir en instance de conteneur GTK. */ -int convert_to_gtk_container(PyObject *, void *); - -#endif - - #if !defined(INCLUDE_GTK_SUPPORT) && !defined(HOMEMADE_RGBA) # define HOMEMADE_RGBA @@ -283,6 +341,21 @@ int convert_to_gdk_rgba(PyObject *, void *); #endif +/* Tableau de chaînes de caractères converti */ +typedef struct _charp_array_t +{ + char **values; /* Liste de chaînes textuelles */ + size_t length; /* Taille de cette liste */ + +} charp_array_t; + +/* Tente de convertir en tableau de chaînes de caractères. */ +int convert_to_sequence_to_charp_array(PyObject *, void *); + +/* Libère de la mémoire un tableau de chaînes de caractères. */ +void clean_charp_array(charp_array_t *); + + /* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */ diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am index bb9ed5d..abadb7f 100644 --- a/plugins/pychrysalide/plugins/Makefile.am +++ b/plugins/pychrysalide/plugins/Makefile.am @@ -2,10 +2,10 @@ noinst_LTLIBRARIES = libpychrysaplugins.la libpychrysaplugins_la_SOURCES = \ - constants.h constants.c \ - plugin.h plugin.c \ module.h module.c \ - translate.h translate.c + plugin.h plugin.c \ + python-int.h \ + python.h python.c libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c deleted file mode 100644 index 7e20e4c..0000000 --- a/plugins/pychrysalide/plugins/constants.c +++ /dev/null @@ -1,147 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * constants.c - ajout des constantes principales - * - * Copyright (C) 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "constants.h" - - -#include <plugins/plugin-def.h> - - -#include "../helpers.h" - - - -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux greffons Python. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_plugin_module_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - result = true; - - values = PyDict_New(); - - if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE); - if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT); - if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED); - if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT); - if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING); - if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME); - if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION); - if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING); - if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER); - if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER); - if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD); - if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED); - if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "PluginAction", values, - "Features with which plugins can extend the core of Chrysalide."); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : arg = argument quelconque à tenter de convertir. * -* dst = destination des valeurs récupérées en cas de succès. * -* * -* Description : Tente de convertir en constante PluginAction. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_plugin_action(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur récupérée */ - - result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); - - switch (result) - { - case -1: - /* L'exception est déjà fixée par Python */ - result = 0; - break; - - case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to PluginAction"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - *((PluginAction *)dst) = value; - break; - - default: - assert(false); - break; - - } - - return result; - -} diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c index 1c7b326..ce94611 100644 --- a/plugins/pychrysalide/plugins/module.c +++ b/plugins/pychrysalide/plugins/module.c @@ -33,8 +33,8 @@ #include <plugins/pglist.h> -#include "constants.h" #include "plugin.h" +#include "python.h" #include "../helpers.h" @@ -46,7 +46,7 @@ static PyObject *py_plugins_get_plugin_by_name(PyObject *, PyObject *); static PyObject *py_plugins_get_all_plugins(PyObject *, PyObject *); /* Fournit les greffons offrant le service demandé. */ -static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); +//static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); @@ -162,7 +162,7 @@ static PyObject *py_plugins_get_all_plugins(PyObject *self, PyObject *args) } - +#if 0 /****************************************************************************** * * * Paramètres : self = NULL car méthode statique. * @@ -219,7 +219,7 @@ static PyObject *py_plugins_get_all_plugins_for_action(PyObject *self, PyObject return result; } - +#endif /****************************************************************************** * * @@ -248,7 +248,7 @@ bool add_plugins_module(PyObject *super) static PyMethodDef py_plugins_methods[] = { PY_PLUGINS_GET_PLUGIN_BY_NAME_METHOD, PY_PLUGINS_GET_ALL_PLUGINS_METHOD, - PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, + //PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, { NULL } }; @@ -293,6 +293,7 @@ bool populate_plugins_module(void) result = true; if (result) result = ensure_python_plugin_module_is_registered(); + if (result) result = ensure_python_python_plugin_is_registered(); assert(result); diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c index de070cb..78f57ba 100644 --- a/plugins/pychrysalide/plugins/plugin.c +++ b/plugins/pychrysalide/plugins/plugin.c @@ -25,22 +25,17 @@ #include "plugin.h" -#include <assert.h> -#include <libgen.h> #include <malloc.h> #include <pygobject.h> #include <string.h> -#include <common/extstr.h> -#include <plugins/dt.h> +#include <common/compiler.h> #include <plugins/plugin-int.h> #include <plugins/pglist.h> #include <plugins/self.h> -#include "constants.h" -#include "translate.h" #include "../access.h" #include "../core.h" #include "../helpers.h" @@ -52,85 +47,53 @@ /* Initialise la classe des greffons d'extension. */ -static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); +static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); -/* Encadre une étape de la vie d'un greffon. */ -static bool py_plugin_module_manage_wrapper(GPluginModule *); - -/* Assiste la désactivation d'un greffon. */ -static bool py_plugin_module_exit(GPluginModule *); - -/* Accompagne la fin du chargement des modules natifs. */ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction); - -/* Fournit le nom brut associé au greffon par défaut. */ -static PyObject *py_plugin_module_get_modname_by_default(PyObject *, PyObject *); +/* Pointe le fichier contenant le greffon manipulé. */ +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *); /* Fournit le nom brut associé au greffon. */ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *); -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - -/* Complète une liste de resources pour thème. */ -static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *); - -/* Rend compte de la création d'un panneau. */ -static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *); - -/* Rend compte d'un affichage ou d'un retrait de panneau. */ -static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool); - -#endif - -/* 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 *); +/* Prend acte de l'activation du greffon. */ +static bool py_plugin_module_enable_wrapper(GPluginModule *); -/* Procède à une opération liée à l'analyse d'un format. */ -static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *); +/* Prend acte de la désactivation du greffon. */ +static bool py_plugin_module_disable_wrapper(GPluginModule *); -/* 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 *); - -#endif +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Affiche un message dans le journal des messages système. */ +static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); -/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Indique le nom associé à un greffon. */ +static PyObject *py_plugin_module_get_name(PyObject *, void *); +/* Fournit une description fonctionnelle d'un greffon. */ +static PyObject *py_plugin_module_get_desc(PyObject *, void *); -/* Construit le nom d'un fichier de configuration du greffon. */ -static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *); +/* Fournit la version d'un greffon et de ses fonctionnalités. */ +static PyObject *py_plugin_module_get_version(PyObject *, void *); -/* Affiche un message dans le journal des messages système. */ -static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); +/* Fournit l'URL des ressources en ligne liées à un greffon. */ +static PyObject *py_plugin_module_get_url(PyObject *, void *); -/* Fournit le nom brut associé au greffon. */ -static PyObject *py_plugin_module_get_modname(PyObject *, void *); +/* Fournit la liste des dépendances d'un greffon donné. */ +static PyObject *py_plugin_module_get_requirements(PyObject *, void *); /* Indique le fichier contenant le greffon manipulé. */ static PyObject *py_plugin_module_get_filename(PyObject *, void *); -/* Fournit la description du greffon dans son intégralité. */ -static PyObject *py_plugin_module_get_interface(PyObject *, void *); +/* Fournit le nom brut associé au greffon. */ +static PyObject *py_plugin_module_get_modname(PyObject *, void *); @@ -141,47 +104,26 @@ static PyObject *py_plugin_module_get_interface(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des greffons d'extension. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) +static int py_plugin_module_init_gclass(GPluginModuleClass *gclass, PyTypeObject *pyclass) { - class->init = NULL; - class->manage = py_plugin_module_manage_wrapper; - class->exit = py_plugin_module_exit; + PY_CLASS_SET_WRAPPER(gclass->get_filename, py_plugin_module_get_filename_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_modname, py_plugin_module_get_modname_wrapper); - class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper; + PY_CLASS_SET_WRAPPER(gclass->enable, py_plugin_module_enable_wrapper); + PY_CLASS_SET_WRAPPER(gclass->disable, py_plugin_module_disable_wrapper); - class->get_modname = py_plugin_module_get_modname_wrapper; - -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - class->include_theme = py_plugin_module_include_theme_wrapper; - class->notify_panel = py_plugin_module_notify_panel_creation_wrapper; - class->notify_docking = py_plugin_module_notify_panel_docking_wrapper; -#endif - - class->handle_content = py_plugin_module_handle_binary_content_wrapper; - class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - - class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper; - class->preload_format = py_plugin_module_preload_binary_format_wrapper; - class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - - class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - - class->detect = py_plugin_module_detect_external_tools_wrapper; - -#endif + return 0; } @@ -202,45 +144,56 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) { + const char *desc; /* Description plus loquace */ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + const char *name; /* Désignation humaine courte */ + charp_array_t required; /* Liste des dépendances */ int ret; /* Bilan d'un appel */ GPluginModule *plugin; /* Greffon à manipuler */ - plugin_interface *iface; /* Interface à constituer */ - GPluginModule *dependency; /* Module nécessaire */ - PyObject *value; /* Valeur à présence imposée */ - size_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ + + static char *kwlist[] = { "name", "desc", "version", "url", "required", NULL }; #define PLUGIN_MODULE_DOC \ - "PythonModule is the class allowing the creation of Chrysalide plugins" \ - " for Python." \ + "PluginModule is the core class handling the Chrysalide plugins." \ + " Python plugins should inherit the pychrysalide.plugins.PythonPlugin" \ + " instead of this one." \ "\n" \ "Calls to the *__init__* constructor of this abstract object expect" \ - " no particular argument.\n" \ + " the following arguments as keyword parameters:\n" \ + "* *name*: a string providing a key name for the plugin;\n" \ + "* *desc* (optional): a string for a human readable description of the" \ + " features provided by the plugin;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n"\ + "* *required* (optional): dependencies of the plugin." \ "\n" \ - "Several items have to be defined as class attributes in the final" \ - " class:\n" \ - "* *_name*: a string providing a small name for the plugin;\n" \ - "* *_desc*: a string for a human readable description of the plugin;\n" \ - "* *_version*: a string providing the version of the plugin;\n" \ - "* *_url*: a string for the homepage describing the plugin;\n" \ - "* *_actions*: a tuple of" \ - " pychrysalide.plugins.PluginModule.PluginAction defining the features" \ - " the plugin is bringing; this list can be empty.\n" \ + "Dependency to the *PyChrysalide* plugin is added automatically if not" \ + " specified.\n" \ "\n" \ - "Depending on the implemented actions, some of the following methods" \ - " have to be defined for new classes:\n" \ - "* pychrysalide.plugins.PluginModule._init_config();\n" \ - "* pychrysalide.plugins.PluginModule._notify_plugins_loaded();\n" \ - "* pychrysalide.plugins.PluginModule._include_theme();\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_creation;\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_docking();\n" \ - "* pychrysalide.plugins.PluginModule._handle_binary_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_loaded_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_format_analysis();\n" \ - "* pychrysalide.plugins.PluginModule._preload_format();\n" \ - "* pychrysalide.plugins.PluginModule._attach_debug_format();\n" \ - "* pychrysalide.plugins.PluginModule._process_disassembly_event();\n" \ - "* pychrysalide.plugins.PluginModule._detect_external_tools()." + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.plugins.PluginModule._get_filename();\n" \ + "* pychrysalide.plugins.PluginModule._get_modname();\n" \ + "* pychrysalide.plugins.PluginModule._enable();\n" \ + "* pychrysalide.plugins.PluginModule._disable()." + + /* Récupération des paramètres */ + + desc = NULL; + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|sssO&", kwlist, + &name, &desc, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + required.values = realloc(required.values, ++required.length * sizeof(char *)); + + required.values[required.length - 1] = strdup("PyChrysalide"); /* Initialisation d'un objet GLib */ @@ -251,105 +204,17 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = calloc(1, sizeof(plugin_interface)); - plugin->interface = iface; - -#define LOAD_PYTHON_IFACE(attr) \ - do \ - { \ - value = PyObject_GetAttrString(self, "_" #attr); \ - if (value == NULL) \ - { \ - PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \ - return -1; \ - } \ - if (PyUnicode_Check(value)) \ - { \ - iface->attr = strdup(PyUnicode_AsUTF8(value)); \ - Py_DECREF(value); \ - } \ - else \ - { \ - Py_DECREF(value); \ - PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \ - return -1; \ - } \ - assert(iface->attr != NULL); \ - } \ - while (0); - - LOAD_PYTHON_IFACE(name); - LOAD_PYTHON_IFACE(desc); - LOAD_PYTHON_IFACE(version); - LOAD_PYTHON_IFACE(url); - - iface->container = false; - - /** - * Comme le greffon n'est pas passé par la résolution des dépendances, - * orchestrée par la fonction g_plugin_module_resolve_dependencies(), - * on simule l'effet attendu en obtenant une référence par un appel à - * get_plugin_by_name(). - * - * L'incrémentation des références doit coller au plus près de - * l'inscription nominative du greffon : en cas de sortie impromptue - * (lorsqu'une erreur intervient pendant un chargement par exemple), - * l'état de l'ensemble est ainsi cohérent au moment du retrait du - * greffon fautif via la fonction g_plugin_module_dispose(). - */ - - lock_plugin_list_for_reading(); - dependency = get_plugin_by_name("PyChrysalide", NULL); - unlock_plugin_list_for_reading(); - - assert(dependency != NULL); - - if (dependency == NULL) - { - PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!")); - return -1; - } - - iface->required = malloc(sizeof(char *)); - iface->required[0] = "PyChrysalide"; - iface->required_count = 1; + STORE_PLUGIN_ABI(plugin); - /* Validation du reste de l'interface */ - - value = PyObject_GetAttrString(self, "_actions"); - - if (value == NULL) + if (!g_plugin_module_create(plugin, name, desc, version, url, + CONST_ARRAY_CAST(required.values, char), required.length)) { - PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing.")); + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create plugin module.")); return -1; } - if (!PyTuple_Check(value)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple.")); - return -1; - } - - iface->actions_count = PyTuple_Size(value); - iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - - for (i = 0; i < iface->actions_count; i++) - { - action = PyTuple_GetItem(value, i); - - if (!PyLong_Check(action)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); - return -1; - } - - iface->actions[i] = PyLong_AsUnsignedLong(action); - - } - - Py_DECREF(value); + clean_charp_array(&required); return 0; @@ -358,56 +223,55 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Encadre une étape de la vie d'un greffon. * +* Description : Pointe le fichier contenant le greffon manipulé. * * * -* Retour : Bilan de l'opération. * +* Retour : Chemin d'accès au greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ + char *result; /* Désignation brute à renvoyer*/ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _manage, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called to react to several steps of the plugin" \ - " life.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *PLUGIN_LOADED*." \ +#define PLUGIN_MODULE_GET_FILENAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_filename, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the filename of the script.\n" \ + "\n" \ + "The result should be the string value pointing to the plugin" \ + " file path.\n" \ ) - result = true; + result = NULL; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_manage")) + if (has_python_method(pyobj, "_get_filename")) { - args = PyTuple_New(1); + pyret = run_python_method(pyobj, "_get_filename", NULL); - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED)); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - pyret = run_python_method(pyobj, "_manage", args); + else + result = strdup(PyUnicode_DATA(pyret)); - result = (pyret == Py_True); + } Py_XDECREF(pyret); - Py_DECREF(args); } @@ -422,44 +286,62 @@ static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à valider. * * * -* Description : Assiste la désactivation d'un greffon. * +* Description : Fournit le nom brut associé au greffon. * * * -* Retour : Bilan de l'opération. * +* Retour : Désignation brute du greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_exit(GPluginModule *plugin) +static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ - plugin_interface *final; /* Interface finale conservée */ + char *result; /* Désignation brute à renvoyer*/ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ - result = true; +#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_modname, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the raw module name of the loaded" \ + " plugin.\n" \ + "\n" \ + "The result should be a short string value.\n" \ +) - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + result = NULL; + + gstate = PyGILState_Ensure(); - if (final != NULL) + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_get_modname")) { - if (final->name != NULL) free(final->name); - if (final->desc != NULL) free(final->desc); - if (final->version != NULL) free(final->version); - if (final->url != NULL) free(final->url); + pyret = run_python_method(pyobj, "_get_modname", NULL); - assert(final->required_count == 1); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - if (final->required != NULL) - free(final->required); + else + result = strdup(PyUnicode_DATA(pyret)); - if (final->actions != NULL) - free(final->actions); + } - free(final); + Py_XDECREF(pyret); } + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + return result; } @@ -468,51 +350,45 @@ static bool py_plugin_module_exit(GPluginModule *plugin) /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * * * -* Description : Accompagne la fin du chargement des modules natifs. * +* Description : Prend acte de l'activation du greffon. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action) +static bool py_plugin_module_enable_wrapper(GPluginModule *plugin) { + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _notify_plugins_loaded, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called once all the (native?) plugins are" \ - " loaded.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \ +#define PLUGIN_MODULE_ENABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _enable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets enabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) + result = true; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_notify_plugins_loaded")) + if (has_python_implementation_method(pyobj, "_enable")) { - args = PyTuple_New(1); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + pyret = run_python_method(pyobj, "_enable", NULL); - pyret = run_python_method(pyobj, "_notify_plugins_loaded", args); + result = (pyret == Py_True); Py_XDECREF(pyret); - Py_DECREF(args); } @@ -520,36 +396,6 @@ static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin PyGILState_Release(gstate); -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Fournit le nom brut associé au greffon par défaut. * -* * -* Retour : Désignation brute du greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - char *path; /* Chemin à traiter */ - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - - path = strdup(g_plugin_module_get_filename(plugin)); - - result = PyUnicode_FromString(basename(path)); - - free(path); - return result; } @@ -557,52 +403,44 @@ static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObjec /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) +static bool py_plugin_module_disable_wrapper(GPluginModule *plugin) { - char *result; /* Désignation brute à renvoyer*/ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF_WITH \ -( \ - _get_modname, "$self, /", \ - METH_VARARGS, py_plugin_module_get_modname_by_default, \ - "(Abstract) method providing the raw module name of the plugin.\n" \ - " loaded.\n" \ - "\n" \ - "The result should be a short string value.\n" \ - "\n" \ - "A default implementation builds the module name from the Python" \ - " script filename." \ +#define PLUGIN_MODULE_DISABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _disable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets disabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) - result = NULL; + result = true; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_get_modname")) + if (has_python_implementation_method(pyobj, "_disable")) { - pyret = run_python_method(pyobj, "_get_modname", NULL); + pyret = run_python_method(pyobj, "_disable", NULL); - if (!PyUnicode_Check(pyret)) - g_plugin_module_log_variadic_message(plugin, LMT_ERROR, - _("The returned raw name must be a string")); - - else - result = strdup(PyUnicode_DATA(pyret)); + result = (pyret == Py_True); Py_XDECREF(pyret); @@ -617,6 +455,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) } + + + + + + + + + #if 0 #ifdef INCLUDE_GTK_SUPPORT @@ -1382,51 +1229,42 @@ static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule * * Paramètres : self = objet Python concerné par l'appel. * * args = arguments fournis à l'appel. * * * -* Description : Construit le nom d'un fichier de configuration du greffon. * +* Description : Affiche un message dans le journal des messages système. * * * -* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. * +* Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - const char *final; /* Suffixe de fichier imposé */ - int create; /* Volonté de création */ - char *filename; /* Nom de fichier déterminé */ + LogMessageType type; /* Espèce du message */ + const char *msg; /* Contenu du message */ -#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \ +#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ ( \ - build_config_filename, "final, /, create=False", \ + log_message, "type, msg, /", \ METH_VARARGS, py_plugin_module, \ - "Build a filename suitable for the plugin configuration, ending with" \ - " the *final* suffix.\n" \ + "Display a message in the log window, in graphical mode, or in the" \ + " console output if none.\n" \ "\n" \ - "If the *create* parameter is set, the path to this filename is" \ - " created.\n" \ + "The type of the message has to be a pychrysalide.core.LogMessageType" \ + " value." \ "\n" \ - "The result is a string or None on failure." \ + "The only difference with the main pychrysalide.core.log_message()" \ + " function is that messages are automatically prefixed with the plugin" \ + " name here." \ ) - create = 0; - - if (!PyArg_ParseTuple(args, "s|p", &final, &create)) + if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) return NULL; - filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create); + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); - if (filename != NULL) - { - result = PyUnicode_FromString(filename); - free(filename); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = Py_None; + Py_INCREF(result); return result; @@ -1435,45 +1273,35 @@ static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject /****************************************************************************** * * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Affiche un message dans le journal des messages système. * +* Description : Indique le nom associé à un greffon. * * * -* Retour : Rien en équivalent Python. * +* Retour : Désignation interne de l'extension, pour référence(s). * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_get_name(PyObject *self, void *closure) { - PyObject *result; /* Bilan à retourner */ - LogMessageType type; /* Espèce du message */ - const char *msg; /* Contenu du message */ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + const char *name; /* Nom attribué au greffon */ -#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ -( \ - log_message, "type, msg, /", \ - METH_VARARGS, py_plugin_module, \ - "Display a message in the log window, in graphical mode, or in the" \ - " console output if none.\n" \ - "\n" \ - "The type of the message has to be a pychrysalide.core.LogMessageType" \ - " value." \ - "\n" \ - "The only difference with the main pychrysalide.core.log_message()" \ - " function is that messages are automatically prefixed with the plugin" \ - " name here." \ +#define PLUGIN_MODULE_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_plugin_module, \ + "Name of the plugin. This string value is used to" \ + " reference the plugin as dependency." \ ) - if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) - return NULL; + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); + name = g_plugin_module_get_name(plugin); - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(name); return result; @@ -1485,32 +1313,31 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit une description fonctionnelle d'un greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Description textuelle associée à une extension ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_desc(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - char *modname; /* Désignation brute */ + const char *desc; /* Description du greffon */ -#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - modname, py_plugin_module, \ - "Raw module name of the plugin." \ +#define PLUGIN_MODULE_DESC_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + desc, py_plugin_module, \ + "Optional plugin description as string or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - modname = g_plugin_module_get_modname(plugin); - result = PyUnicode_FromString(modname); + desc = g_plugin_module_get_desc(plugin); - free(modname); + result = PyUnicode_FromString(desc); return result; @@ -1522,30 +1349,32 @@ static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Indique le fichier contenant le greffon manipulé. * +* Description : Fournit la version d'un greffon et de ses fonctionnalités. * * * -* Retour : Chemin d'accès au greffon. * +* Retour : Version sous forme de chaîne de caractères ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_version(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const char *filename; /* Chemin d'accès associé */ + const char *version; /* Version du greffon */ -#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_VERSION_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - filename, py_plugin_module, \ - "Filename of the plugin." \ + version, py_plugin_module, \ + "Optional plugin version, in free format, as string," \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - filename = g_plugin_module_get_filename(plugin); - result = PyUnicode_FromString(filename); + version = g_plugin_module_get_version(plugin); + + result = PyUnicode_FromString(version); return result; @@ -1557,113 +1386,204 @@ static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la description du greffon dans son intégralité. * +* Description : Fournit l'URL des ressources en ligne liées à un greffon. * * * -* Retour : Interfaçage renseigné. * +* Retour : URL de renvoi associée à une extension ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_interface(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_url(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const plugin_interface *iface; /* Interface liée à traduire */ + const char *url; /* URL associée au greffon */ -#define PLUGIN_MODULE_INTERFACE_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_URL_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - interface, py_plugin_module, \ - "Interface exported by the plugin..\n" \ - "\n" \ - "This property is a pychrysalide.StructObject instance." \ - "\n" \ - "The provided information is composed of the following" \ - " properties :\n" \ - "\n" \ - "* gtp_name;\n" \ - "* name;\n" \ - "* desc;\n" \ - "* version;\n" \ - "* url;\n" \ - "* container;\n" \ - "* required;\n" \ - "* actions.\n" \ - "\n" \ - "The *gtp_name* value may be *None* for non-native plugin." \ - " All other fields carry a string value except:\n" \ - "* *container*: a boolean status indicating if the plugin" \ - " can embed other plugins;\n" \ - "* *required*: a tuple of depedencies names;\n" \ - "* *actions*: a tuple of available features from the plugin"\ - " coded as pychrysalide.plugins.PluginModule.PluginAction" \ - " values." \ + url, py_plugin_module, \ + "Optional URL pointing to the plugin homepage as string" \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = g_plugin_module_get_interface(plugin); - result = translate_plugin_interface_to_python(iface); + url = g_plugin_module_get_url(plugin); + + result = PyUnicode_FromString(url); return result; } -#if 0 + /****************************************************************************** * * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la configuration mise en place pour le greffon. * +* Description : Fournit la liste des dépendances d'un greffon donné. * * * -* Retour : Configuration dédiée à l'extension. * +* Retour : Liste des noms d'extensions requises pour une extension. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_requirements(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - GGenConfig *config; /* Configuration associée */ + const char * const *required; /* Liste de dépendances */ + size_t count; /* Nombre de ces dépendances */ + size_t i; /* Boucle de parcours */ -#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_REQUIREMENTS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - config, py_plugin_module, \ - "Dedicated configuration for the plugin." \ - "\n" \ - "The value is a pychrysalide.glibext.GenConfig instance" \ - " or None if the configuration is not yet created.\n" \ - "\n" \ - "As configuration storage path depends on the plugin name," \ - " all plugin properties have to get fully loaded by the" \ - " core before the configuration can be setup." \ - "automatically" \ + requirements, py_plugin_module, \ + "Tuple of the plugin dependencies." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + required = g_plugin_module_get_requirements(plugin, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + PyTuple_SetItem(result, i, PyUnicode_FromString(required[i])); + + return result; + +} + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *filename; /* Chemin d'accès associé */ + +#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + filename, py_plugin_module, \ + "Filename of the plugin." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + filename = g_plugin_module_get_filename(plugin); + + if (filename != NULL) + { + result = PyUnicode_FromString(filename); + + free(filename); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as filename")); + + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *modname; /* Désignation brute */ + +#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + modname, py_plugin_module, \ + "Raw module name of the plugin." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - config = g_plugin_module_get_config(plugin); - if (config == NULL) + modname = g_plugin_module_get_modname(plugin); + + if (modname != NULL) { - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(modname); + + free(modname); + } else { - result = pygobject_new(G_OBJECT(config)); + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as modname")); - g_object_unref(G_OBJECT(config)); + result = NULL; } return result; } -#endif /****************************************************************************** @@ -1681,33 +1601,22 @@ static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) PyTypeObject *get_python_plugin_module_type(void) { static PyMethodDef py_plugin_module_methods[] = { - PLUGIN_MODULE_MANAGE_WRAPPER, - PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER, + PLUGIN_MODULE_GET_FILENAME_WRAPPER, PLUGIN_MODULE_GET_MODNAME_WRAPPER, -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - PLUGIN_MODULE_INCLUDE_THEME_WRAPPER, - PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER, - PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER, -#endif - PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER, - PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER, - PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER, - PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER, - PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER, - PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD, -#endif + PLUGIN_MODULE_ENABLE_WRAPPER, + PLUGIN_MODULE_DISABLE_WRAPPER, PLUGIN_MODULE_LOG_MESSAGE_METHOD, { NULL } }; static PyGetSetDef py_plugin_module_getseters[] = { - PLUGIN_MODULE_MODNAME_ATTRIB, + PLUGIN_MODULE_NAME_ATTRIB, + PLUGIN_MODULE_DESC_ATTRIB, + PLUGIN_MODULE_VERSION_ATTRIB, + PLUGIN_MODULE_URL_ATTRIB, + PLUGIN_MODULE_REQUIREMENTS_ATTRIB, PLUGIN_MODULE_FILENAME_ATTRIB, - PLUGIN_MODULE_INTERFACE_ATTRIB, - //PLUGIN_MODULE_CONFIG_ATTRIB, + PLUGIN_MODULE_MODNAME_ATTRIB, { NULL } }; @@ -1761,10 +1670,9 @@ bool ensure_python_plugin_module_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) - return false; + pyg_register_class_init(G_TYPE_PLUGIN_MODULE, (PyGClassInitFunc)py_plugin_module_init_gclass); - if (!define_plugin_module_constants(type)) + if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) return false; } @@ -1772,80 +1680,3 @@ bool ensure_python_plugin_module_is_registered(void) return true; } - - -/****************************************************************************** -* * -* 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 *create_python_plugin(const char *modname, const char *filename) -{ - GPluginModule *result; /* Structure à retourner */ - PyObject *name; /* Chemin d'accès pour Python */ - PyObject *module; /* Script Python chargé */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - - name = PyUnicode_FromString(modname); - if (name == NULL) goto bad_exit; - - module = PyImport_Import(name); - Py_DECREF(name); - - if (module == NULL) goto no_import; - - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); - - if (class == NULL) goto no_class; - if (!PyType_Check(class->ob_type)) goto no_class; - - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto no_instance; - - result = G_PLUGIN_MODULE(pygobject_get(instance)); - - result->filename = strdup(filename); - - /** - * L'instance Python et l'objet GLib résultante sont un même PyGObject. - * - * Donc pas besoin de toucher au comptage des références ici, la libération - * se réalisera à la fin, quand l'objet GLib sera libéré. - */ - - Py_DECREF(module); - - return result; - - no_instance: - - log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); - - no_class: - - if (class == NULL) - log_plugin_simple_message(LMT_ERROR, - _("An error occured when looking for the 'AutoLoad': item not found!")); - - no_import: - - Py_XDECREF(module); - - log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - - bad_exit: - - return NULL; - -} diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h index ad54b8e..fd3d239 100644 --- a/plugins/pychrysalide/plugins/plugin.h +++ b/plugins/pychrysalide/plugins/plugin.h @@ -27,13 +27,9 @@ #include <Python.h> -#include <glib-object.h> #include <stdbool.h> -#include <plugins/plugin.h> - - /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_plugin_module_type(void); @@ -41,9 +37,6 @@ PyTypeObject *get_python_plugin_module_type(void); /* Prend en charge l'objet 'pychrysalide.plugins.PluginModule'. */ bool ensure_python_plugin_module_is_registered(void); -/* Crée un greffon à partir de code Python. */ -GPluginModule *create_python_plugin(const char *, const char *); - #endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */ diff --git a/plugins/pychrysalide/plugins/python-int.h b/plugins/pychrysalide/plugins/python-int.h new file mode 100644 index 0000000..7408d18 --- /dev/null +++ b/plugins/pychrysalide/plugins/python-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python-int.h - prototypes internes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 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_PLUGINS_PYTHON_INT_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H + + +#include "python.h" + + +#include <plugins/plugin-int.h> + + + +/* Greffon Python pour Chrysalide (instance) */ +struct _GPythonPlugin +{ + GPluginModule parent; /* A laisser en premier */ + + char *file; /* Valeur initiale de __file__ */ + +}; + + +/* Greffon Python pour Chrysalide (classe) */ +struct _GPythonPluginClass +{ + GPluginModuleClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un greffon Python. */ +bool g_python_plugin_create(GPythonPlugin *, const char *, const char *, const char *, const char *, char ***, size_t *, const char *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H */ diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c new file mode 100644 index 0000000..d6b9281 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.c @@ -0,0 +1,489 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.c - déclinaison Python de greffons + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "python.h" + + +#include <malloc.h> +#include <pygobject.h> + + +#include <common/compiler.h> +#include <common/cpp.h> + + +#include "plugin.h" +#include "python-int.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +/* Initialise la classe des greffons Python. */ +static void g_python_plugin_class_init(GPythonPluginClass *); + +/* Initialise une instance de greffon Python. */ +static void g_python_plugin_init(GPythonPlugin *); + +/* Supprime toutes les références externes. */ +static void g_python_plugin_dispose(GPythonPlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_python_plugin_finalize(GPythonPlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Pointe le fichier contenant le greffon manipulé. */ +static char *g_python_plugin_get_filename(const GPythonPlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_python_plugin_get_modname(const GPythonPlugin *); + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->get_filename = (get_plugin_filename_fc)g_python_plugin_get_filename; + plugin->get_modname = (get_plugin_modname_fc)g_python_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); + + plugin->file = NULL; + +} + + +/****************************************************************************** +* * +* 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) +{ + 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) +{ + if (plugin->file != NULL) + free(plugin->file); + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * +* file = emplacement du script considéré. * +* * +* Description : Met en place un greffon Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_python_plugin_create(GPythonPlugin *plugin, const char *name, const char *desc, const char *version, const char *url, char ***required, size_t *count, const char *file) +{ + bool result; /* Bilan à retourner */ + + /* Ajout imposé d'une dépendance à Python */ + + *required = realloc(*required, ++(*count) * sizeof(char *)); + + (*required)[*count - 1] = strdup("PyChrysalide"); + + /* Poursuite de la mise en place */ + + result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), + name, desc, version, url, + CONST_ARRAY_CAST(*required, char), *count); + + if (result) + plugin->file = strdup(file); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Pointe le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_filename(const GPythonPlugin *plugin) +{ + char *result; /* Chemin d'accès à renvoyer */ + + result = strdup(plugin->file); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_modname(const GPythonPlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *filename; /* Chemin du script Python */ + char *name; /* Nom du fichier associé */ + size_t length; /* Taille du nom */ + int ret; /* Bilan d'une comparaison */ + + filename = g_python_plugin_get_filename(plugin); + + name = basename(filename); + + length = strlen(name); + +#define PYTHON_SUFFIX ".py" + + ret = strncmp(&name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)], + PYTHON_SUFFIX, + STATIC_STR_SIZE(PYTHON_SUFFIX)); + + if (ret == 0) + name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)] = '\0'; + + result = strdup(name); + + free(filename); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + charp_array_t required; /* Liste des dépendances */ + const char *file; /* Emplacement du script */ + int ret; /* Bilan de lecture des args. */ + PyObject *value; /* Valeur d'un arg. implicite */ + const char *name; /* Désignation humaine courte */ + const char *desc; /* Description plus loquace */ + GPythonPlugin *plugin; /* Greffon à manipuler */ + + static char *kwlist[] = { "file", "version", "url", "required", NULL }; + +#define PYTHON_PLUGIN_DOC \ + "The PythonPlugin class helps to build custom Python plugins:\n" \ + "* some required information (*name* and *desc* for the parent" \ + " constructor) is automatically extracted from the final class name" \ + " or its documentation;\n" \ + "* implementations for pychrysalide.plugins.PluginModule._get_filename()" \ + " and pychrysalide.plugins.PluginModule._get_modname() are provided," \ + " relying on the *file* argument.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect the" \ + " following arguments as keyword parameters:\n" \ + "* *file*: path to the Python script; the value should be equal to the" \ + " *__file__* keyword;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n" \ + "* *required* (optional): dependencies of the plugin." + + /* Récupération des paramètres */ + + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|ssO&", kwlist, + &file, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + name = self->ob_type->tp_name; + + value = PyObject_GetAttrString(self, "__doc__"); + + if (value != NULL && PyUnicode_Check(value)) + desc = PyUnicode_AsUTF8(value); + else + desc = NULL; + + Py_XDECREF(value); + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + plugin = G_PYTHON_PLUGIN(pygobject_get(self)); + + STORE_PLUGIN_ABI(plugin); + + if (!g_python_plugin_create(plugin, name, desc, version, url, + &required.values, &required.length, + file)) + { + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create Python plugin.")); + return -1; + } + + clean_charp_array(&required); + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION POUR SUPPORT PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_python_plugin_type(void) +{ + static PyMethodDef py_python_plugin_methods[] = { + { NULL } + }; + + static PyGetSetDef py_python_plugin_getseters[] = { + { NULL } + }; + + static PyTypeObject py_python_plugin_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.PythonPlugin", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = PYTHON_PLUGIN_DOC, + + .tp_methods = py_python_plugin_methods, + .tp_getset = py_python_plugin_getseters, + + .tp_init = py_python_plugin_init, + .tp_new = py_python_plugin_new, + + }; + + return &py_python_plugin_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_python_plugin_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'PythonPlugin' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_python_plugin_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_plugin_module_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/plugins/python.h b/plugins/pychrysalide/plugins/python.h new file mode 100644 index 0000000..8613bd1 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.h - prototypes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H + + +#include <Python.h> +#include <stdbool.h> + + +#include <glibext/helpers.h> + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type()) + +DECLARE_GTYPE(GPythonPlugin, g_python_plugin, G, PYTHON_PLUGIN); + + + +/* ------------------------- DEFINITION POUR SUPPORT PYTHON ------------------------- */ + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_python_plugin_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. */ +bool ensure_python_python_plugin_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H */ diff --git a/plugins/pychrysalide/plugins/translate.c b/plugins/pychrysalide/plugins/translate.c deleted file mode 100644 index 055788c..0000000 --- a/plugins/pychrysalide/plugins/translate.c +++ /dev/null @@ -1,110 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * translate.c - conversion de structures liées aux greffons en objets Python - * - * Copyright (C) 2021 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "translate.h" - - -#include <assert.h> - - -#include "plugin.h" -#include "../helpers.h" -#include "../struct.h" - - - -/****************************************************************************** -* * -* Paramètres : iface = ensemble d'informations à transcrire en Python. * -* * -* Description : Traduit une description d'interface de greffon. * -* * -* Retour : Structure mise en place ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyObject *translate_plugin_interface_to_python(const plugin_interface *iface) -{ - PyObject *result; /* Construction à retourner */ - PyTypeObject *base; /* Modèle d'objet à créer */ - bool status; /* Bilan d'une traduction */ - PyObject *array; /* Tableau à insérer */ - size_t i; /* Boucle de parcours */ - PyTypeObject *itype; /* Type d'élément à créer */ - PyObject *item; /* Elément mis en place */ - - base = get_python_py_struct_type(); - - result = PyObject_CallFunction((PyObject *)base, NULL); - assert(result != NULL); - - status = true; - - if (status) status = TRANSLATE_STRING_FIELD(result, iface, gtp_name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, desc); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, version); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, url); - - if (status) status = TRANSLATE_BOOLEAN_FIELD(result, iface, container); - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, required, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->required_count; i++) - { - item = PyUnicode_FromString(iface->required[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, actions, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->actions_count; i++) - { - item = cast_with_constants_group_from_type(itype, "PluginAction", iface->actions[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (!status) - { - Py_DECREF(result); - result = NULL; - } - - return result; - -} |