instructions after the start of the disassembly. The destination would be
preceded by a line with just ``l42:``.
+``branch`` and ``absbranch`` fields can additionally have a ``call="true"``
+attribute. For now, this just changes the disassembly. In particular the label
+prefix is changed to ``fxn`` and an extra empty line before the destination is
+added to visually seperate the disassembly into functions. So, for example, a
+call instruction defined like this:
+
+.. code-block:: xml
+
+ <bitset name="call" extends="#instruction">
+ <display>
+ call #{OFFSET}
+ </display>
+ <pattern low="26" high="31">110010</pattern> <!-- opcode goes here -->
+ <field name="OFFSET" low="0" high="25" type="branch" call="true"/>
+ </bitset>
+
+will disassemble to ``call #fxn42``.
+
+Finally, users with special knowledge about where execution may start can define
+"entrypoints" when disassembling which are printed like function call
+destinations, with an extra empty line, but with an arbitrary user-defined
+name. Names that are ``fxn`` or ``l`` followed by a number are discouraged
+because they may clash with automatically-generated names.
+
Encoding
--------
BITSET_WORD *branch_targets;
/**
+ * Bitset of instructions that are call targets.
+ */
+ BITSET_WORD *call_targets;
+
+ /**
+ * Bitset of instructions that are entrypoints.
+ */
+ BITSET_WORD *entrypoints;
+
+ /**
* We allow a limited amount of expression evaluation recursion, but
* not recursive evaluation of any given expression, to prevent infinite
* recursion.
*/
struct decode_scope *scope;
+ /* Next entrypoint to be decoded. */
+ struct isa_entrypoint *next_entrypoint;
+
+ /* Sentinel value after the last entrypoint in the array. */
+ struct isa_entrypoint *end_entrypoint;
+
/**
* A small fixed upper limit on # of decode errors to capture per-
* instruction seems reasonable.
offset = val;
}
if (offset < scope->state->num_instr) {
- print(scope->state, "l%d", offset);
- BITSET_SET(scope->state->branch_targets, offset);
+ if (field->call) {
+ print(scope->state, "fxn%d", offset);
+ BITSET_SET(scope->state->call_targets, offset);
+ } else {
+ print(scope->state, "l%d", offset);
+ BITSET_SET(scope->state->branch_targets, offset);
+ }
break;
}
}
break;
}
- if (state->options->branch_labels &&
- BITSET_TEST(state->branch_targets, state->n)) {
- if (state->options->instr_cb) {
- state->options->instr_cb(state->options->cbdata,
- state->n, instr.bitset);
+ if (state->options->branch_labels) {
+ bool entrypoint = state->next_entrypoint !=
+ state->end_entrypoint &&
+ state->next_entrypoint->offset == state->n;
+
+ /* Print an extra empty line before functions and
+ * entrypoints to more clearly separate them.
+ */
+ if ((BITSET_TEST(state->call_targets, state->n) || entrypoint) &&
+ state->n != 0) {
+ if (state->options->instr_cb) {
+ state->options->instr_cb(state->options->cbdata,
+ state->n, instr.bitset);
+ }
+ print(state, "\n");
+ }
+
+ while (state->next_entrypoint != state->end_entrypoint &&
+ state->next_entrypoint->offset == state->n) {
+ if (state->options->instr_cb) {
+ state->options->instr_cb(state->options->cbdata,
+ state->n, instr.bitset);
+ }
+ print(state, "%s:\n", state->next_entrypoint->name);
+ state->next_entrypoint++;
+ }
+
+ if (BITSET_TEST(state->call_targets, state->n)) {
+ if (state->options->instr_cb) {
+ state->options->instr_cb(state->options->cbdata,
+ state->n, instr.bitset);
+ }
+ print(state, "fxn%d:\n", state->n);
+ }
+
+ if (BITSET_TEST(state->branch_targets, state->n)) {
+ if (state->options->instr_cb) {
+ state->options->instr_cb(state->options->cbdata,
+ state->n, instr.bitset);
+ }
+ print(state, "l%d:\n", state->n);
}
- print(state, "l%d:\n", state->n);
}
if (state->options->instr_cb) {
}
}
+static int
+cmp_entrypoints(const void *_a, const void *_b)
+{
+ const struct isa_entrypoint *a = _a, *b = _b;
+ return (int)a->offset - (int)b->offset;
+}
+
void
isa_decode(void *bin, int sz, FILE *out, const struct isa_decode_options *options)
{
if (state->options->branch_labels) {
state->branch_targets = rzalloc_size(state,
sizeof(BITSET_WORD) * BITSET_WORDS(state->num_instr));
+ state->call_targets = rzalloc_size(state,
+ sizeof(BITSET_WORD) * BITSET_WORDS(state->num_instr));
/* Do a pre-pass to find all the branch targets: */
state->out = fopen("/dev/null", "w");
if (options) {
state->options = options;
}
+
+ /* Sort the entrypoints by offset and initialize entrypoint
+ * state.
+ */
+ if (options->entrypoint_count) {
+ struct isa_entrypoint *entrypoints =
+ ralloc_array(state, struct isa_entrypoint,
+ options->entrypoint_count);
+ memcpy(entrypoints, options->entrypoints,
+ options->entrypoint_count * sizeof(*entrypoints));
+ qsort(entrypoints, options->entrypoint_count,
+ sizeof(*entrypoints), cmp_entrypoints);
+ state->next_entrypoint = entrypoints;
+ state->end_entrypoint = entrypoints + options->entrypoint_count;
+ }
}
state->out = out;