From f8039890a4df1051ec9e2a00f6bf2ff4a432ebb8 Mon Sep 17 00:00:00 2001 From: Dmitry Selyutin Date: Mon, 11 Sep 2023 00:03:25 +0300 Subject: [PATCH] libopid: introduce assembly sketch --- src/libopid/Makefile | 4 ++ src/libopid/codegen.py | 122 ++++++++++++++++++++++++++++++++++++++++- src/libopid/opid-asm.c | 58 ++++++++++++++++++++ src/libopid/opid-opc.c | 32 +++++++++++ src/libopid/opid.h | 18 ++++++ 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 src/libopid/opid-asm.c diff --git a/src/libopid/Makefile b/src/libopid/Makefile index 21ccf94b..8fcc4c1d 100644 --- a/src/libopid/Makefile +++ b/src/libopid/Makefile @@ -6,6 +6,7 @@ PYTHON?=python3 GENS:=\ + opid-asm-gen.c \ opid-dis-gen.c \ opid-opc-gen.c \ @@ -14,6 +15,7 @@ SRCS:=\ opid-check.c \ OBJS:=\ + opid-asm.o \ opid-dis.o \ opid-opc.o \ opid-check.o \ @@ -56,8 +58,10 @@ libopid.a: $(OBJS) $(AR) rcs $@ $(OBJS) +opid-asm.c: opid-asm-gen.c opid-dis.c: opid-dis-gen.c opid-opc.c: opid-opc-gen.c +opid-asm.o: opid-asm.c opid-asm-gen.c opid-dis.o: opid-dis.c opid-dis-gen.c opid-opc.o: opid-opc.c opid-opc-gen.c diff --git a/src/libopid/codegen.py b/src/libopid/codegen.py index f02ceb55..7df84daf 100644 --- a/src/libopid/codegen.py +++ b/src/libopid/codegen.py @@ -62,6 +62,7 @@ def wrap(bits): class Mode(enum.Enum): + ASM_GEN_C = "opid-asm-gen.c" DIS_GEN_C = "opid-dis-gen.c" OPC_GEN_C = "opid-opc-gen.c" @@ -73,6 +74,7 @@ class Mode(enum.Enum): cache = Cache() codegen = { + Mode.ASM_GEN_C: AsmGenSource, Mode.DIS_GEN_C: DisGenSource, Mode.OPC_GEN_C: OpcGenSource, }[self](cache=cache, **arguments) @@ -321,6 +323,124 @@ class Record(Struct): dynamic_operand_ids: DynamicOperandIds +class AsmGenSource(Source): + @mdis.dispatcher.Hook(DynamicOperands) + @contextlib.contextmanager + def dispatch_operands(self, node): + self.emit("static inline enum opid_state") + self.emit("opid_assemble_operand(uint64_t *insn,") + with self: + with self: + self.emit("size_t category,") + self.emit("struct opid_operand *operand,") + self.emit("void *uctx,") + self.emit("enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) {") + self.emit("enum opid_state state;") + self.emit("") + self.emit("state = operand_cb(uctx, operand);") + self.emit("if (state != OPID_SUCCESS)") + with self: + self.emit("return state;") + self.emit("") + self.emit(f"switch (category) {{") + yield node + self.emit("default:") + with self: + self.emit("return OPID_ERROR_OPERAND_0_LOOKUP;") + self.emit("}") + self.emit("") + with self: + self.emit("return OPID_SUCCESS;") + self.emit("}") + + @mdis.dispatcher.Hook(DynamicOperand) + @contextlib.contextmanager + def dispatch_operand(self, node, *, path, pathcls): + def generic_handler(span, flags="UINT64_C(0)"): + limit = f"UINT64_C({((1 << len(span)) - 1)})" + yield f"if (operand->value > {limit})" + with self: + yield "return OPID_ERROR_OPERAND_0_RANGE;" + yield f"*insn |= (" + with self: + yield from store(span) + yield f");" + yield f"operand->flags = {flags};" + self.emit("break;") + + def nonzero_handler(span): + yield "--operand->value;" + yield "*insn |= (" + with self: + yield from store(span) + yield ");" + yield "++operand->value;" + yield "operand->flags = OPID_OPERAND_NONZERO;" + self.emit("break;") + + def signed_handler(span, flags="OPID_OPERAND_SIGNED"): + limit = ((1 << len(span)) - 1) + lolimit = f"INT64_C(-{((limit // 2) + 1)})" + hilimit = f"INT64_C({(limit // 2)})" + yield f"if (((int64_t)operand->value < {lolimit}) ||" + with self: + with self: + yield f"((int64_t)operand->value > {hilimit}))" + yield "return OPID_ERROR_OPERAND_0_RANGE;" + yield f"*insn |= (" + with self: + yield from store(span) + yield f");" + yield f"operand->flags = {flags};" + self.emit("break;") + + def address_handler(span): + yield from signed_handler(span, "(OPID_OPERAND_ADDRESS | OPID_OPERAND_SIGNED)") + + def gpr_handler(span, pair=False): + if not pair: + yield from generic_handler(span, "OPID_OPERAND_GPR") + else: + yield from generic_handler(span, "(OPID_OPERAND_GPR | OPID_OPERAND_PAIR)") + + def fpr_handler(span, pair=False): + if not pair: + yield from generic_handler(span, "OPID_OPERAND_FPR") + else: + yield from generic_handler(span, "(OPID_OPERAND_FPR | OPID_OPERAND_PAIR)") + + def cr3_handler(span): + yield from generic_handler(span, "OPID_OPERAND_CR3") + + def cr5_handler(span): + yield from generic_handler(span, "OPID_OPERAND_CR5") + + handlers = { + insndb.GPRPairOperand: lambda span: gpr_handler(span, True), + insndb.FPRPairOperand: lambda span: fpr_handler(span, True), + insndb.GPROperand: gpr_handler, + insndb.FPROperand: fpr_handler, + insndb.CR3Operand: cr3_handler, + insndb.CR5Operand: cr5_handler, + insndb.TargetAddrOperand: address_handler, + insndb.SignedOperand: signed_handler, + insndb.NonZeroOperand: nonzero_handler, + insndb.DynamicOperand: generic_handler, + object: None, + } + self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */") + with self: + for (cls, handler) in handlers.items(): + if issubclass(node.cls, cls): + break + if handler is None: + raise ValueError("unknown handler") + for line in handler(span=node.span): + self.emit(line) + self.emit("") + yield node + + class DisGenSource(Source): class Walker(Walker): @mdis.dispatcher.Hook(DynamicOperand, RecordTable) @@ -503,7 +623,7 @@ class OpcGenSource(Source): @mdis.dispatcher.Hook(OpcodeTable) @contextlib.contextmanager - def dispatch_potable(self, node): + def dispatch_opcode_table(self, node): heads = ([0] * (1 << 6)) tails = ([0] * (1 << 6)) for (index, counter) in enumerate(itertools.accumulate(node)): diff --git a/src/libopid/opid-asm.c b/src/libopid/opid-asm.c new file mode 100644 index 00000000..98234be8 --- /dev/null +++ b/src/libopid/opid-asm.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#include "opid.h" + +static inline enum opid_state +opid_assemble_operand(uint64_t *insn, + size_t category, + struct opid_operand *operand, + void *uctx, + enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)); + +#include "opid-asm-gen.c" + +static inline enum opid_state +opid_assemble_operands(struct opid_ctx *ctx, + struct opid_record const *record, + uint64_t *insn, + void *uctx, + enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) { + enum opid_state state; + struct opid_opcode const *opcode = &record->opcode; + + *insn = (opcode->value & opcode->mask); + + for (size_t id = 0; ((id != OPID_OPERANDS_NR) && record->operands[id]); ++id) { + state = opid_assemble_operand(insn, + record->operands[id], + &ctx->operands[id], + uctx, + operand_cb); + if (state != OPID_SUCCESS) + return state; + } + + return state; +} + +enum opid_state +opid_assemble(struct opid_ctx *ctx, + char const *name, + uint64_t *insn, + void *uctx, + enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)) { + struct opid_record const *record; + + record = opid_lookup_name(name); + if (record == NULL) + return OPID_ERROR_LOOKUP; + + _Static_assert(sizeof(record->name) == sizeof(ctx->name), + "record name length does not match context name length"); + memcpy(ctx->name, record->name, sizeof(record->name)); + + return opid_assemble_operands(ctx, record, insn, uctx, operand_cb); +} diff --git a/src/libopid/opid-opc.c b/src/libopid/opid-opc.c index df827711..9cebf609 100644 --- a/src/libopid/opid-opc.c +++ b/src/libopid/opid-opc.c @@ -1,5 +1,6 @@ #include #include +#include #include "opid.h" @@ -37,3 +38,34 @@ opid_lookup_insn(uint64_t insn) { return NULL; } + +static struct opid_name_id const * +opid_lookup_name_id(char const *name) { + size_t head = 0U; + size_t tail = ((sizeof(opid_name_id_table) / sizeof(*opid_name_id_table)) - 1U); + + while (head != tail) { + size_t iter = (head + ((tail - head) / 2U)); + int state = strcmp(opid_name_id_table[iter].name, name); + + if (state < 0) + head = (iter + 1); + else if (state > 0) + tail = iter; + else + return &opid_name_id_table[iter]; + } + + return NULL; +} + +struct opid_record const * +opid_lookup_name(char const *name) { + struct opid_name_id const *name_id; + + name_id = opid_lookup_name_id(name); + if (name_id == NULL) + return NULL; + + return name_id->record; +} diff --git a/src/libopid/opid.h b/src/libopid/opid.h index 9f064aff..341f4742 100644 --- a/src/libopid/opid.h +++ b/src/libopid/opid.h @@ -14,6 +14,14 @@ enum opid_state { OPID_ERROR_OPERAND_5_LOOKUP, OPID_ERROR_OPERAND_6_LOOKUP, OPID_ERROR_OPERAND_7_LOOKUP, + OPID_ERROR_OPERAND_0_RANGE, + OPID_ERROR_OPERAND_1_RANGE, + OPID_ERROR_OPERAND_2_RANGE, + OPID_ERROR_OPERAND_3_RANGE, + OPID_ERROR_OPERAND_4_RANGE, + OPID_ERROR_OPERAND_5_RANGE, + OPID_ERROR_OPERAND_6_RANGE, + OPID_ERROR_OPERAND_7_RANGE, }; #define OPID_NAME_BYTES 16 @@ -56,8 +64,18 @@ struct opid_ctx { ((id != OPID_OPERANDS_NR) && (id != (ctx)->nr_operands))); \ operand = &(ctx)->operands[++id]) +enum opid_state +opid_assemble(struct opid_ctx *ctx, + char const *name, + uint64_t *insn, + void *uctx, + enum opid_state (*operand_cb)(void *uctx, struct opid_operand *operand)); + enum opid_state opid_disassemble(struct opid_ctx *ctx, uint64_t insn); struct opid_record const * opid_lookup_insn(uint64_t insn); + +struct opid_record const * +opid_lookup_name(char const *name); -- 2.30.2