blob: 23b687ec1efeb3b85e6f4bd67b3a6706a29593af [file] [edit]
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <fadec.h>
#include <string.h>
static PyObject* DecodeError;
static unsigned Reg_FromPair(unsigned idx, unsigned ty) {
return idx == FD_REG_NONE ? 0 : idx | (ty << 8);
}
static PyObject* Reg_AsLong(unsigned reg) {
return Py_BuildValue(&"I"[!reg], reg);
}
typedef struct {
PyObject_HEAD
uint8_t size;
uint8_t type;
uint8_t idx;
} RegObject;
typedef struct {
PyObject_HEAD
FdInstr fdi;
} InstObject;
typedef struct {
PyObject_HEAD
InstObject* inst;
unsigned idx;
} OpObject;
static PyObject* Reg_str(RegObject* self) {
char buf[16];
fd_format_reg(self->type, self->idx, self->size, buf);
return Py_BuildValue("s", buf);
}
static PyObject* Reg_repr(RegObject* self) {
char buf[16];
fd_format_reg(self->type, self->idx, self->size, buf);
char repr[256];
sprintf(repr, "<fadec.Reg \"%s\",size=%u,type=%u,idx=%u>", buf, self->size,
self->type, self->idx);
return Py_BuildValue("s", repr);
}
static PyMemberDef RegType_members[] = {
{"sizelg", Py_T_UBYTE, offsetof(RegObject, size), Py_READONLY,
"Register size (logarithmic)."},
{"type", Py_T_UBYTE, offsetof(RegObject, type), Py_READONLY,
"Register type."},
{"idx", Py_T_UBYTE, offsetof(RegObject, idx), Py_READONLY,
"Register index."},
{0}};
static PyTypeObject RegType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fadec.Reg",
.tp_doc = "A register",
.tp_basicsize = sizeof(RegObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_str = (reprfunc)Reg_str,
.tp_repr = (reprfunc)Reg_repr,
.tp_members = RegType_members,
};
static void Op_dealloc(PyObject* self) {
Py_XDECREF(((OpObject*)self)->inst);
Py_TYPE(self)->tp_free(self);
}
static PyObject* Op_repr(OpObject* self) {
char buf[128];
fd_format(&self->inst->fdi, buf, sizeof buf);
char repr[256];
sprintf(repr, "<fadec.Op \"%s\",idx=%u>", buf, self->idx);
return Py_BuildValue("s", repr);
}
static PyObject* Op_getis_type(OpObject* self, void* closure) {
return PyBool_FromLong(FD_OP_TYPE(&self->inst->fdi, self->idx) ==
(uintptr_t)closure);
}
static PyObject* Op_getsize(OpObject* self, void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_OP_SIZE(&self->inst->fdi, self->idx));
}
static PyObject* Op_getreg(OpObject* self, void* Py_UNUSED(closure)) {
if (FD_OP_TYPE(&self->inst->fdi, self->idx) != FD_OT_REG) {
PyErr_SetString(PyExc_AttributeError, "reg");
return NULL;
}
RegObject* reg = (RegObject*)PyType_GenericAlloc(&RegType, 0);
if (reg) {
reg->size = FD_OP_SIZELG(&self->inst->fdi, self->idx);
reg->type = FD_OP_REG_TYPE(&self->inst->fdi, self->idx);
reg->idx = FD_OP_REG(&self->inst->fdi, self->idx);
}
return (PyObject*)reg;
}
static PyObject* Op_getimm(OpObject* self, void* closure) {
FdOpType exp_ty = *(const char*)closure == 'o' ? FD_OT_OFF : FD_OT_IMM;
if (FD_OP_TYPE(&self->inst->fdi, self->idx) != exp_ty) {
PyErr_SetString(PyExc_AttributeError, (const char*)closure);
return NULL;
}
return PyLong_FromLong(FD_OP_IMM(&self->inst->fdi, self->idx));
}
static PyObject* Op_getbase(OpObject* self, void* Py_UNUSED(closure)) {
FdOpType ot = FD_OP_TYPE(&self->inst->fdi, self->idx);
if (ot != FD_OT_MEM && ot != FD_OT_MEMBCST) {
PyErr_SetString(PyExc_AttributeError, "base");
return NULL;
}
if (FD_OP_BASE(&self->inst->fdi, self->idx) == FD_REG_NONE)
Py_RETURN_NONE;
RegObject* reg = (RegObject*)PyType_GenericAlloc(&RegType, 0);
if (reg) {
reg->size = FD_ADDRSIZELG(&self->inst->fdi);
reg->type = FD_RT_GPL;
reg->idx = FD_OP_BASE(&self->inst->fdi, self->idx);
}
return (PyObject*)reg;
}
static PyObject* Op_getindex(OpObject* self, void* Py_UNUSED(closure)) {
FdOpType ot = FD_OP_TYPE(&self->inst->fdi, self->idx);
if (ot != FD_OT_MEM && ot != FD_OT_MEMBCST) {
PyErr_SetString(PyExc_AttributeError, "base");
return NULL;
}
if (FD_OP_INDEX(&self->inst->fdi, self->idx) == FD_REG_NONE)
Py_RETURN_NONE;
RegObject* reg = (RegObject*)PyType_GenericAlloc(&RegType, 0);
if (reg) {
// TODO: VSIB
reg->size = FD_ADDRSIZELG(&self->inst->fdi);
reg->type = FD_RT_GPL;
reg->idx = FD_OP_INDEX(&self->inst->fdi, self->idx);
}
return (PyObject*)reg;
}
static PyObject* Op_getscale(OpObject* self, void* Py_UNUSED(closure)) {
FdOpType ot = FD_OP_TYPE(&self->inst->fdi, self->idx);
if ((ot != FD_OT_MEM && ot != FD_OT_MEMBCST) ||
FD_OP_INDEX(&self->inst->fdi, self->idx) == FD_REG_NONE) {
PyErr_SetString(PyExc_AttributeError, "scale");
return NULL;
}
return PyLong_FromLong(FD_OP_SCALE(&self->inst->fdi, self->idx));
}
static PyObject* Op_getdisp(OpObject* self, void* Py_UNUSED(closure)) {
FdOpType ot = FD_OP_TYPE(&self->inst->fdi, self->idx);
if ((ot != FD_OT_MEM && ot != FD_OT_MEMBCST)) {
PyErr_SetString(PyExc_AttributeError, "disp");
return NULL;
}
return PyLong_FromLong(FD_OP_DISP(&self->inst->fdi, self->idx));
}
static PyObject* Op_getbcstsz(OpObject* self, void* Py_UNUSED(closure)) {
if (FD_OP_TYPE(&self->inst->fdi, self->idx) != FD_OT_MEMBCST) {
PyErr_SetString(PyExc_AttributeError, "bcstsz");
return NULL;
}
return PyLong_FromLong(FD_OP_BCSTSZ(&self->inst->fdi, self->idx));
}
static PyGetSetDef OpType_getset[] = {
{"is_reg", (getter)Op_getis_type, NULL, "Whether operand is a register",
(void*)FD_OT_REG},
{"is_imm", (getter)Op_getis_type, NULL, "Whether operand is an immediate",
(void*)FD_OT_IMM},
{"is_mem", (getter)Op_getis_type, NULL,
"Whether operand refers to a memory location", (void*)FD_OT_MEM},
{"is_off", (getter)Op_getis_type, NULL,
"Whether operand is relative offset", (void*)FD_OT_OFF},
{"is_membcst", (getter)Op_getis_type, NULL,
"Whether operand refers to a broadcasted memory location",
(void*)FD_OT_MEMBCST},
{"size", (getter)Op_getsize, NULL, "Operand size (in bytes, or zero)",
NULL},
{"reg", (getter)Op_getreg, NULL, "Register value (for register operands)",
NULL},
{"imm", (getter)Op_getimm, NULL,
"Immediate operand (for immediate operands)", (void*)"imm"},
{"off", (getter)Op_getimm, NULL, "Offset operand (for offset operands)",
(void*)"off"},
{"base", (getter)Op_getbase, NULL, "Base register (for memory operands)",
NULL},
{"index", (getter)Op_getindex, NULL, "Index register (for memory operands)",
NULL},
{"scale", (getter)Op_getscale, NULL, "Scale (for memory operands)", NULL},
{"disp", (getter)Op_getdisp, NULL, "Displacement (for memory operands)",
NULL},
{"bcstsz", (getter)Op_getbcstsz, NULL, "Broadcast size (in bytes)", NULL},
{0}};
static PyTypeObject OpType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fadec.Op",
.tp_doc = "An instruction operand",
.tp_basicsize = sizeof(OpObject),
.tp_itemsize = 0,
.tp_dealloc = Op_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_repr = (reprfunc)Op_repr,
.tp_getset = OpType_getset,
};
static PyObject* Inst_str(InstObject* self) {
char buf[128];
fd_format(&self->fdi, buf, sizeof buf);
return Py_BuildValue("s", buf);
}
static PyObject* Inst_repr(InstObject* self) {
char buf[128];
fd_format(&self->fdi, buf, sizeof buf);
char repr[256];
sprintf(repr, "<fadec.Inst \"%s\",size=%u>", buf, FD_SIZE(&self->fdi));
return Py_BuildValue("s", repr);
}
static PyObject* Inst_gettype(InstObject* self, void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_TYPE(&self->fdi));
}
static PyObject* Inst_getsize(InstObject* self, void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_SIZE(&self->fdi));
}
static PyObject* Inst_getsegment(InstObject* self, void* Py_UNUSED(closure)) {
return Reg_AsLong(Reg_FromPair(FD_SEGMENT(&self->fdi), FD_RT_SEG));
}
static PyObject* Inst_getopsize(InstObject* self, void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_OPSIZE(&self->fdi));
}
static PyObject* Inst_getaddrsize(InstObject* self, void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_ADDRSIZE(&self->fdi));
}
static PyObject* Inst_gethas_rep(InstObject* self, void* Py_UNUSED(closure)) {
return PyBool_FromLong(FD_HAS_REP(&self->fdi));
}
static PyObject* Inst_gethas_repnz(InstObject* self, void* Py_UNUSED(closure)) {
return PyBool_FromLong(FD_HAS_REPNZ(&self->fdi));
}
static PyObject* Inst_gethas_lock(InstObject* self, void* Py_UNUSED(closure)) {
return PyBool_FromLong(FD_HAS_LOCK(&self->fdi));
}
static PyObject* Inst_gethas_3e(InstObject* self, void* Py_UNUSED(closure)) {
return PyBool_FromLong(FD_HAS_3E(&self->fdi));
}
static PyObject* Inst_getmaskreg(InstObject* self, void* Py_UNUSED(closure)) {
unsigned maskreg =
FD_MASKREG(&self->fdi) ? FD_MASKREG(&self->fdi) : FD_REG_NONE;
return Reg_AsLong(Reg_FromPair(maskreg, FD_RT_MASK));
}
static PyObject* Inst_getmaskzero(InstObject* self, void* Py_UNUSED(closure)) {
if (FD_MASKREG(&self->fdi) == 0) {
PyErr_SetString(PyExc_AttributeError, "maskzero");
return NULL;
}
return PyBool_FromLong(FD_MASKZERO(&self->fdi));
}
static PyObject* Inst_getops(InstObject* self, void* Py_UNUSED(closure)) {
const FdInstr* fdi = &self->fdi;
unsigned num_ops = 0;
for (; num_ops < 4; num_ops++)
if (FD_OP_TYPE(fdi, num_ops) == FD_OT_NONE)
break;
PyObject* res = PyTuple_New(num_ops);
for (unsigned idx = 0; idx < num_ops; idx++) {
OpObject* op = (OpObject*)PyType_GenericAlloc(&OpType, 0);
if (op == NULL) {
Py_DECREF(res);
return NULL;
}
Py_INCREF(self);
op->inst = self;
op->idx = idx;
PyTuple_SetItem(res, idx, (PyObject*)op);
}
return res;
}
static PyObject* Inst_getroundcontrol(InstObject* self,
void* Py_UNUSED(closure)) {
return PyLong_FromLong(FD_ROUNDCONTROL(&self->fdi));
}
static PyGetSetDef InstType_getset[] = {
{"type", (getter)Inst_gettype, NULL,
"The type/mnemonic of the instruction.", NULL},
{"size", (getter)Inst_getsize, NULL,
"The size of the instruction in bytes.", NULL},
{"segment", (getter)Inst_getsegment, NULL,
"The specified segment override register, or None.", NULL},
{"addrsize", (getter)Inst_getaddrsize, NULL,
"The address size attribute of the instruction in bytes.", NULL},
{"opsize", (getter)Inst_getopsize, NULL,
"The operation width in bytes of the instruction if this is not encoded "
"in the operands, for example for the string instruction (e.g. MOVS).",
NULL},
{"has_rep", (getter)Inst_gethas_rep, NULL,
"Whether the instruction was encoded with a REP prefix. Needed for: "
"(1) Handling the instructions MOVS, STOS, LODS, INS and OUTS properly. "
"(2) Handling the instructions SCAS and CMPS, for which this means REPZ.",
NULL},
{"has_repnz", (getter)Inst_gethas_repnz, NULL,
"Whether the instruction was encoded with a REPNZ prefix.", NULL},
{"has_lock", (getter)Inst_gethas_lock, NULL,
"Whether the instruction was encoded with a LOCK prefix.", NULL},
{"has_3e", (getter)Inst_gethas_3e, NULL,
"whether there is a meaningful 3E prefix used for indirect JMP (notrack "
"prefix) and conditional branches (hint-taken prefix).",
NULL},
{"maskreg", (getter)Inst_getmaskreg, NULL,
"The opmask register for EVEX-encoded instructions; None for no mask.",
NULL},
{"maskzero", (getter)Inst_getmaskzero, NULL,
"Whether zero masking shall be used.", NULL},
{"roundcontrol", (getter)Inst_getroundcontrol, NULL,
"The rounding mode for EVEX-encoded instructions.", NULL},
{"ops", (getter)Inst_getops, NULL, "The instruction operands.", NULL},
{0}};
static PyTypeObject InstType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fadec.Inst",
.tp_doc = "A decoded instruction",
.tp_basicsize = sizeof(InstObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_str = (reprfunc)Inst_str,
.tp_repr = (reprfunc)Inst_repr,
.tp_getset = InstType_getset,
};
static PyObject* decode_generic(PyObject* Py_UNUSED(self), PyObject* args,
int mode) {
Py_buffer buf;
if (!PyArg_ParseTuple(args, "y*", &buf))
return NULL;
InstObject* inst = (InstObject*)PyType_GenericAlloc(&InstType, 0);
if (inst != NULL) {
int res = fd_decode(buf.buf, buf.len, mode, 0, &inst->fdi);
if (res < 0) {
PyErr_SetString(DecodeError, "Decoding failed");
Py_DECREF(inst);
PyBuffer_Release(&buf);
return NULL;
}
}
PyBuffer_Release(&buf);
return (PyObject*)inst;
}
static PyObject* decode32(PyObject* self, PyObject* args) {
return decode_generic(self, args, 32);
}
static PyObject* decode64(PyObject* self, PyObject* args) {
return decode_generic(self, args, 64);
}
static PyMethodDef FadecMethods[] = {
{"decode32", decode32, METH_VARARGS, "Decode for 32-bit mode."},
{"decode64", decode64, METH_VARARGS, "Decode for 64-bit mode."},
{0}};
static struct PyModuleDef fadecmodule = {
PyModuleDef_HEAD_INIT,
"fadec",
"Fadec: fast decoder/encoder for x86(-64)",
-1,
FadecMethods,
};
static int add_const(PyObject* dict, const char* name, unsigned value) {
PyObject* val = PyLong_FromLong(value);
if (val == NULL)
return -1;
if (PyDict_SetItemString(dict, name, val) < 0)
return -1;
Py_DECREF(val);
return 0;
}
static PyObject* create_reg_dict(void) {
// TODO: reduce code size
PyObject* reg_dict = PyDict_New();
#define REG_TY(ty) \
if (add_const(reg_dict, #ty, FD_RT_##ty) < 0) \
goto fail;
REG_TY(VEC)
REG_TY(GPL)
REG_TY(GPH)
REG_TY(SEG)
REG_TY(FPU)
REG_TY(MMX)
REG_TY(TMM)
REG_TY(MASK)
REG_TY(BND)
REG_TY(CR)
REG_TY(DR)
#undef REG_TY
return reg_dict;
fail:
Py_DECREF(reg_dict);
return NULL;
}
static PyObject* create_inst_dict(void) {
// TODO: reduce code size
PyObject* inst_dict = PyDict_New();
#define FD_MNEMONIC(name, value) \
if (add_const(inst_dict, #name, value) < 0) \
goto fail;
#include <fadec-decode-public.inc>
#undef FD_MNEMONIC
return inst_dict;
fail:
Py_DECREF(inst_dict);
return NULL;
}
PyMODINIT_FUNC PyInit_fadec(void) {
if (PyType_Ready(&OpType) < 0)
return NULL;
Py_XDECREF(RegType.tp_dict);
RegType.tp_dict = create_reg_dict();
if (!RegType.tp_dict)
return NULL;
if (PyType_Ready(&RegType) < 0) {
Py_CLEAR(RegType.tp_dict);
return NULL;
}
Py_XDECREF(InstType.tp_dict);
InstType.tp_dict = create_inst_dict();
if (!InstType.tp_dict)
return NULL;
if (PyType_Ready(&InstType) < 0) {
Py_CLEAR(InstType.tp_dict);
return NULL;
}
PyObject* m = PyModule_Create(&fadecmodule);
if (m == NULL)
return NULL;
Py_INCREF(&InstType);
if (PyModule_AddObject(m, "Inst", (PyObject*)&InstType) < 0) {
Py_DECREF(&InstType);
Py_DECREF(m);
return NULL;
}
Py_INCREF(&OpType);
if (PyModule_AddObject(m, "Op", (PyObject*)&OpType) < 0) {
Py_DECREF(&OpType);
Py_DECREF(m);
return NULL;
}
Py_INCREF(&RegType);
if (PyModule_AddObject(m, "Reg", (PyObject*)&RegType) < 0) {
Py_DECREF(&RegType);
Py_DECREF(m);
return NULL;
}
DecodeError = PyErr_NewException("fadec.DecodeError", NULL, NULL);
if (DecodeError == NULL) {
Py_DECREF(m);
return NULL;
}
Py_XINCREF(DecodeError);
if (PyModule_AddObject(m, "DecodeError", DecodeError) < 0) {
Py_XDECREF(DecodeError);
Py_CLEAR(DecodeError);
Py_DECREF(m);
return NULL;
}
return m;
}