#ifndef __PYWRAPS_HPP__ #define __PYWRAPS_HPP__ //------------------------------------------------------------------------ // Types #ifndef PYUL_DEFINED #define PYUL_DEFINED typedef unsigned PY_LONG_LONG PY_ULONG_LONG; #ifdef __EA64__ typedef unsigned PY_LONG_LONG pyul_t; typedef PY_LONG_LONG pyl_t; #else typedef unsigned long pyul_t; typedef long pyl_t; #endif #endif #ifdef __EA64__ #define PY_FMT64 "K" #define PY_SFMT64 "L" #else #define PY_FMT64 "k" #define PY_SFMT64 "l" #endif //------------------------------------------------------------------------ #define S_IDAAPI_MODNAME "idaapi" #define S_IDC_MODNAME "idc" #define S_IDAAPI_EXECSCRIPT "IDAPython_ExecScript" #define S_IDAAPI_COMPLETION "IDAPython_Completion" #define S_IDAAPI_FORMATEXC "IDAPython_FormatExc" #define S_IDAAPI_LOADPROCMOD "IDAPython_LoadProcMod" #define S_IDAAPI_UNLOADPROCMOD "IDAPython_UnLoadProcMod" //------------------------------------------------------------------------ // PyIdc conversion object IDs #define PY_ICID_INT64 0 #define PY_ICID_BYREF 1 #define PY_ICID_OPAQUE 2 //------------------------------------------------------------------------ // Constants used with the notify_when() #define NW_OPENIDB 0x0001 #define NW_OPENIDB_SLOT 0 #define NW_CLOSEIDB 0x0002 #define NW_CLOSEIDB_SLOT 1 #define NW_INITIDA 0x0004 #define NW_INITIDA_SLOT 2 #define NW_TERMIDA 0x0008 #define NW_TERMIDA_SLOT 3 #define NW_REMOVE 0x0010 // Uninstall flag #define NW_EVENTSCNT 4 // Count of notify_when codes //------------------------------------------------------------------------ // Constants used by the pyvar_to_idcvar and idcvar_to_pyvar functions #define CIP_FAILED -1 // Conversion error #define CIP_IMMUTABLE 0 // Immutable object passed. Will not update the object but no error occured #define CIP_OK 1 // Success #define CIP_OK_OPAQUE 2 // Success, but the data pointed to by the PyObject* is an opaque object. //--------------------------------------------------------------------------- class gil_lock_t { private: PyGILState_STATE state; public: gil_lock_t() { state = PyGILState_Ensure(); } ~gil_lock_t() { PyGILState_Release(state); } }; // Declare a variable to acquire/release the GIL #define PYW_GIL_GET gil_lock_t lock; #define GIL_CHKCONDFAIL (((debug & IDA_DEBUG_PLUGIN) == IDA_DEBUG_PLUGIN) \ && PyGILState_GetThisThreadState() != _PyThreadState_Current) #define PYW_GIL_CHECK_LOCKED_SCOPE() \ do \ { \ if ( GIL_CHKCONDFAIL ) \ { \ msg("*** WARNING: Code at %s:%d should have the GIL, but apparently doesn't ***\n", \ __FILE__, __LINE__); \ if ( under_debugger ) \ BPT; \ } \ } while ( false ) //------------------------------------------------------------------------- struct exc_report_t { ~exc_report_t() { if ( PyErr_Occurred() ) PyErr_Print(); } }; #define PYW_GIL_GET_AND_REPORT_ERROR PYW_GIL_GET; exc_report_t exc; //------------------------------------------------------------------------ // All the exported functions from PyWraps are forward declared here insn_t *insn_t_get_clink(PyObject *self); op_t *op_t_get_clink(PyObject *self); //------------------------------------------------------------------------- // The base for a reference. Will automatically increase the reference // counter for the object when it is assigned from another ref_t, // and decrease the reference counter when destroyed. // This is meant to be used whenever possible, in order to prevent // situations where, e.g., a given code path is taken and we return from // a function without first decreasing the reference counter. // // Note: You should never, ever have to Py_[INCREF|DECREF] the 'o' object yourself. // Note: These simple ref_t cannot be created with a PyObject* directly // (that would be the role of 'newref_t'/'borref_t' below.) // In other words: simple 'ref_t' instances are never created from the // result of calling the CPython API. They are only used when in // idapython land. // In yet other words: the CPython API only deals in terms of // 'New references' and 'Borrowed references'. Those are implemented, // respectively, by the 'newref_t' and 'borref_t' classes below. // This 'ref_t' is only used for internal handling. struct ref_t { PyObject *o; ref_t() : o(NULL) {} ref_t(const ref_t &other) : o(other.o) { incref(); } ~ref_t() { decref(); } ref_t &operator=(const ref_t &other) { // We *must* first (possibly) set & incref the other object, // because decref() might call the Python's deallocator, which // might have side-effects, that might affect this ref_t // instance. // If that's too many 'might' to your taste, let me illustrate. // // py_plgform.hpp's 'plgform_t' holds a 'ref_t' instance, named 'py_obj'. // If the actual, Qt widget wrapped by that plgform_t gets destroyed, // plgform_t::unhook() will be called, which will assign an // empty ref_t instance to its 'py_obj'. // That will decrement the refcount, and might call the deallocator: // the plgform_t::destroy static function. // That function will 'delete' the plgform_t object. // But, in the ~plgform_t() destructor, the 'py_obj' object will be // destroyed too: decreasing once again the refcnt (which now falls to -1). // At this point, all hell breaks loose (or is allowed to). PyObject *was = o; o = other.o; incref(); if ( was != NULL ) Py_DECREF(was); return *this; } void incref() const { if ( o != NULL ) Py_INCREF(o); } void decref() const { if ( o != NULL ) { QASSERT(30469, o->ob_refcnt > 0); Py_DECREF(o); } } bool operator==(PyObject *other) const { return o == other; } bool operator!=(PyObject *other) const { return ! ((*this) == other); } bool operator==(const ref_t &other) const { return o == other.o; } bool operator!=(const ref_t &other) const { return ! ((*this) == other); } }; //------------------------------------------------------------------------- // A 'new' reference. Typically used when the CPython implementation returns // a PyObject* whose refcnt was already increased, and that the caller is // responsible for releasing. // // This implements the 'New reference' idea at http://docs.python.org/2/c-api/intro.html: // --- // "When a function passes ownership of a reference on to its caller, // the caller is said to receive a new reference" // --- // E.g., from "PyObject_GetAttrString"'s doc: // --- // "Return value: New reference. // Retrieve an attribute named attr_name from object o[...]" // --- struct newref_t : public ref_t { newref_t(); // No. newref_t(const newref_t &other); // No. newref_t &operator=(const newref_t &other); // No. newref_t(PyObject *_o) { #ifdef _DEBUG QASSERT(30409, _o == NULL || _o->ob_refcnt >= 1); #endif o = _o; } }; //------------------------------------------------------------------------- // A 'borrowed' reference. Typically used when the CPython implementation returns // a PyObject* whose ownership is _not_ transferred to the caller. // Therefore, and since the caller wants to make sure the object is not // released while it is using it, it must first increase the reference count, // and then decrease it. // // This is similar to the simpler 'ref_t' in that it first increases, and then // decreases the reference count. The difference is that 'borref_t' instances // can be created with a PyObject*, while 'ref_t' instances cannot (by design). // // This implements the 'Borrowed reference' idea at http://docs.python.org/2/c-api/intro.html: // --- // "When no ownership is transferred, the caller is said to borrow the reference. // Nothing needs to be done for a borrowed reference." // --- struct borref_t : public ref_t { borref_t(); // No. borref_t(const newref_t &other); // No. borref_t &operator=(const newref_t &other); // No. borref_t(PyObject *_o) { o = _o; incref(); // ~ref_t() will decref(), so we need to incref. } }; //------------------------------------------------------------------------ // Vector of ref_t struct ref_vec_t : public qvector { void to_pyobject_pointers(qvector *out) { size_t n = size(); out->resize(n); for ( size_t i = 0; i < n; ++i ) out->at(i) = at(i).o; } }; // Returns a new reference to a class // Return value: New reference. ref_t get_idaapi_attr(const char *attr); // Returns a new reference to a class by its ID // Return value: New reference. ref_t get_idaapi_attr_by_id(const int class_id); // Tries to import a module and swallows the exception if it fails and returns NULL // Return value: New reference. ref_t PyW_TryImportModule(const char *name); // Tries to get an attribute and swallows the exception if it fails and returns NULL ref_t PyW_TryGetAttrString(PyObject *py_var, const char *attr); // Returns the linked object (void *) from a PyObject void *pyobj_get_clink(PyObject *pyobj); // Converts a Python number (LONGLONG or normal integer) to an IDC variable (VT_LONG or VT_INT64) bool PyW_GetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); // Returns a qstring from a Python attribute string bool PyW_GetStringAttr( PyObject *py_obj, const char *attr_name, qstring *str); // Converts a Python number to an uint64 and indicates whether the number was a long number bool PyW_GetNumber(PyObject *py_var, uint64 *num, bool *is_64 = NULL); // Checks if an Python object can be treated like a sequence bool PyW_IsSequenceType(PyObject *obj); // Returns an error string from the last exception (and clears it) bool PyW_GetError(qstring *out = NULL, bool clear_err = true); bool PyW_GetError(char *buf, size_t bufsz, bool clear_err = true); // If an error occured (it calls PyGetError) it displays it and return TRUE // This function is used when calling callbacks bool PyW_ShowCbErr(const char *cb_name); // Utility function to create a class instance whose constructor takes zero arguments ref_t create_idaapi_class_instance0(const char *clsname); // Utility function to create linked class instances ref_t create_idaapi_linked_class_instance(const char *clsname, void *lnk); // Returns the string representation of a PyObject bool PyW_ObjectToString(PyObject *obj, qstring *out); // Utility function to convert a python object to an IDC object // and sets a python exception on failure. bool pyvar_to_idcvar_or_error(const ref_t &py_obj, idc_value_t *idc_obj); // Creates and initializes an IDC exception error_t PyW_CreateIdcException(idc_value_t *res, const char *msg); // // Conversion functions // bool pyw_convert_idc_args( const idc_value_t args[], int nargs, ref_vec_t &pargs, bool as_tupple, char *errbuf = NULL, size_t errbufsize = 0); // Converts Python variable to IDC variable // gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF int pyvar_to_idcvar( const ref_t &py_var, idc_value_t *idc_var, int *gvar_sn = NULL); // Converts from IDC to Python // We support converting VT_REF IDC variable types int idcvar_to_pyvar( const idc_value_t &idc_var, ref_t *py_var); // Walks a Python list or Sequence and calls the callback Py_ssize_t pyvar_walk_list( const ref_t &py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud) = NULL, void *ud = NULL); Py_ssize_t pyvar_walk_list( PyObject *py_list, int (idaapi *cb)(const ref_t &py_item, Py_ssize_t index, void *ud) = NULL, void *ud = NULL); // Converts an intvec_t to a Python list object ref_t PyW_IntVecToPyList(const intvec_t &intvec); // Converts an Python list to an intvec bool PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec); // Converts a Python list to a qstrvec bool PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec); //------------------------------------------------------------------------- PyObject *qstrvec2pylist(qstrvec_t &vec); //------------------------------------------------------------------------- inline bool PyWStringOrNone_Check(PyObject *tp) { return tp == Py_None || PyString_Check(tp); } //------------------------------------------------------------------------- inline const p_list * PyW_Fields(PyObject *tp) { return tp == Py_None ? NULL : (const p_list *) PyString_AsString(tp); } //--------------------------------------------------------------------------- // // notify_when() // bool pywraps_nw_term(); bool pywraps_nw_notify(int slot, ...); bool pywraps_nw_init(); //--------------------------------------------------------------------------- bool pywraps_check_autoscripts(char *buf, size_t bufsize); // [De]Initializes PyWraps bool init_pywraps(); void deinit_pywraps(); void hexrays_clear_python_cfuncptr_t_references(void); #endif