forked from idapython/src
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pywraps.hpp
401 lines (340 loc) · 13.9 KB
/
pywraps.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
#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<ref_t>
{
void to_pyobject_pointers(qvector<PyObject*> *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);
void free_compiled_form_instances(void);
//#define PYGDBG_ENABLED
#ifdef PYGDBG_ENABLED
#define PYGLOG(...) msg(__VA_ARGS__)
#else
#define PYGLOG(...)
#endif
//-------------------------------------------------------------------------
struct pycall_res_t
{
pycall_res_t(PyObject *pyo);
~pycall_res_t();
inline bool success() const { return result.o != NULL; }
newref_t result;
private:
pycall_res_t(); // No.
};
#endif