Esempio n. 1
0
static int get_isa(Context * ctx, ContextAddress addr, ContextISA * isa) {
    if (context_get_isa(ctx, addr, isa) < 0) {
        memset(isa, 0, sizeof(ContextISA));
        return -1;
    }
#if ENABLE_MemoryMap
    if (isa->size == 0) {
        unsigned i, j;
        MemoryMap * client_map = NULL;
        MemoryMap * target_map = NULL;
        Context * mem = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
        if (memory_map_get(mem, &client_map, &target_map) == 0) {
            isa->addr = addr;
            isa->size = ~addr + 1;
            for (j = 0; j < 2; j++) {
                MemoryMap * map = j ? target_map : client_map;
                for (i = 0; i < map->region_cnt; i++) {
                    MemoryRegion * r = map->regions + i;
                    ContextAddress x = r->addr;
                    ContextAddress y = r->addr + r->size;
                    if (x > addr && (addr + isa->size == 0 || x < addr + isa->size)) isa->size = x - addr;
                    if (y > addr && (addr + isa->size == 0 || y < addr + isa->size)) isa->size = y - addr;
                }
            }
        }
    }
#endif
    return 0;
}
Esempio n. 2
0
static void command_disassemble(char * token, Channel * c) {
    int error = 0;
    Context * ctx = NULL;
    DisassembleCmdArgs * args = (DisassembleCmdArgs *)loc_alloc_zero(sizeof(DisassembleCmdArgs));
    json_read_string(&c->inp, args->id, sizeof(args->id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    args->addr = (ContextAddress)json_read_uint64(&c->inp);
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    args->size = (ContextAddress)json_read_uint64(&c->inp);
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    json_read_struct(&c->inp, read_disassembly_params, args);
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    ctx = id2ctx(args->id);
    if (ctx == NULL) error = ERR_INV_CONTEXT;
    else if (ctx->exited) error = ERR_ALREADY_EXITED;
    else if (context_get_group(ctx, CONTEXT_GROUP_PROCESS)->mem_access == 0) error = ERR_INV_CONTEXT;

    if (error != 0) {
        write_stringz(&c->out, "R");
        write_stringz(&c->out, token);
        write_errno(&c->out, error);
        write_stringz(&c->out, "null");
        write_stream(&c->out, MARKER_EOM);
        loc_free(args);
    }
    else {
        channel_lock(args->c = c);
        strlcpy(args->token, token, sizeof(args->token));
        post_safe_event(ctx, safe_event_disassemble, args);
    }
}
Esempio n. 3
0
int cpu_bp_on_resume(Context * ctx, int * single_step) {
    ContextExtensionARM * ext = EXT(ctx);
    ContextExtensionARM * bps = EXT(context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT));
    if (ctx->stopped_by_cb != NULL || ext->hw_bps_regs_generation != bps->hw_bps_generation) {
        if (set_debug_regs(ctx, single_step) < 0) return -1;
    }
    return 0;
}
Esempio n. 4
0
int cpu_bp_on_suspend(Context * ctx, int * triggered) {
    unsigned cb_cnt = 0;
    ContextExtensionARM * ext = EXT(ctx);
    ContextExtensionARM * bps = EXT(context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT));

    if (ctx->exiting) return 0;

    if (bps->bp_cnt > 0 || bps->wp_cnt > 0) {
        int i;
        ContextAddress pc = 0;

        if (read_reg(ctx, pc_def, pc_def->size, &pc) < 0) return -1;
        if (ext->skip_wp_addr != pc) ext->skip_wp_set = 0;

        if (bps->bp_cnt > 0) {
            for (i = 0; i < bps->bp_cnt; i++) {
                ContextBreakpoint * cb = bps->hw_bps[i];
                if (cb != NULL && ((uint32_t)cb->address & ~0x1) == pc && (ext->armed & (1u << i))) {
                    ext->triggered_hw_bps[cb_cnt++] = cb;
                }
            }
        }

        if (bps->wp_cnt > 0) {
            siginfo_t siginfo;
            pid_t pid = id2pid(ctx->id, NULL);
            if (ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo) < 0) return -1;
            if (siginfo.si_signo == SIGTRAP && (siginfo.si_code & 0xffff) == 0x0004 && siginfo.si_errno < 0) {
                /* Watchpoint */
                for (i = bps->bp_cnt; i < bps->bp_cnt + bps->wp_cnt; i++) {
                    ContextBreakpoint * cb = bps->hw_bps[i];
                    if (cb != NULL && (ext->armed & (1u << i))) {
                        if (bps->wp_cnt > 1) {
                            ContextAddress addr = (ContextAddress)siginfo.si_addr;
                            if (addr < cb->address || addr >= cb->address + cb->length) continue;
                        }
                        ext->triggered_hw_bps[cb_cnt++] = cb;
                        ext->skip_wp_set |= 1u << i;
                        ext->skip_wp_addr = pc;
                    }
                }
            }
        }
        if (cb_cnt > 0) {
            ctx->stopped_by_cb = ext->triggered_hw_bps;
            ctx->stopped_by_cb[cb_cnt] = NULL;
        }
    }

    *triggered = cb_cnt > 0;
    return 0;
}
Esempio n. 5
0
int cpu_bp_plant(ContextBreakpoint * bp) {
    Context * ctx = bp->ctx;
    ContextExtensionARM * bps = EXT(ctx);
    assert(bp->access_types);
    assert(ctx == context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT));
    if (get_bp_info(ctx) < 0) return -1;
    if (bp->access_types & CTX_BP_ACCESS_VIRTUAL) {
        if (bp->access_types & CTX_BP_ACCESS_INSTRUCTION) {
            unsigned i;
            unsigned n = 0;
            for (i = 0; i < bps->bp_cnt; i++) {
                assert(bps->hw_bps[i] != bp);
                if (bps->hw_bps[i] == NULL) {
                    bps->hw_bps[i] = bp;
                    bps->hw_bps_generation++;
                    n++;
                    break;
                }
            }
            if (n == 0) {
                clear_bp(bp);
                errno = ERR_UNSUPPORTED;
                return -1;
            }
        }
        if (bp->access_types & (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE)) {
            unsigned n = 0;
            if (bp->length <= bps->wp_size) {
                unsigned i;
                for (i = bps->bp_cnt; i < bps->bp_cnt + bps->wp_cnt; i++) {
                    assert(bps->hw_bps[i] != bp);
                    if (bps->hw_bps[i] == NULL) {
                        bps->hw_bps[i] = bp;
                        bps->hw_bps_generation++;
                        n++;
                        break;
                    }
                }
            }
            if (n == 0) {
                clear_bp(bp);
                errno = ERR_UNSUPPORTED;
                return -1;
            }
        }
        return 0;
    }
    errno = ERR_UNSUPPORTED;
    return -1;
}
Esempio n. 6
0
int cpu_bp_get_capabilities(Context * ctx) {
    int res = 0;
    ContextExtensionARM * bps = EXT(ctx);
    if (ctx != context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT)) return 0;
    if (get_bp_info(ctx) < 0) return 0;
    if (bps->bp_cnt > 0) {
        res |= CTX_BP_ACCESS_INSTRUCTION;
    }
    if (bps->wp_cnt > 0) {
        res |= CTX_BP_ACCESS_DATA_READ;
        res |= CTX_BP_ACCESS_DATA_WRITE;
    }
    res |= CTX_BP_ACCESS_VIRTUAL;
    return res;
}
Esempio n. 7
0
void add_disassembler(Context * ctx, const char * isa, Disassembler disassembler) {
    DisassemblerInfo * i = NULL;
    ContextExtensionDS * ext = EXT(ctx);
    assert(ctx == context_get_group(ctx, CONTEXT_GROUP_CPU));
    if ((i = find_disassembler_info(ctx, isa)) == NULL) {
        if (ext->disassemblers_cnt >= ext->disassemblers_max) {
            ext->disassemblers_max += 8;
            ext->disassemblers = (DisassemblerInfo *)loc_realloc(ext->disassemblers,
                sizeof(DisassemblerInfo) * ext->disassemblers_max);
        }
        i = ext->disassemblers + ext->disassemblers_cnt++;
        i->isa = loc_strdup(isa);
    }
    i->disassembler = disassembler;
}
Esempio n. 8
0
static void command_get_capabilities_cache_client(void * x) {
    int error = 0;
    Context * ctx = NULL;
    ContextExtensionDS * ext = NULL;
    GetCapabilitiesCmdArgs * args = (GetCapabilitiesCmdArgs *)x;
    Channel * c = cache_channel();

    ctx = id2ctx(args->id);
    if (ctx == NULL) error = ERR_INV_CONTEXT;
    else if (ctx->exited) error = ERR_ALREADY_EXITED;
    else ext = EXT(context_get_group(ctx, CONTEXT_GROUP_CPU));

    cache_exit();

    if (!is_channel_closed(c)) {
        OutputStream * out = &c->out;
        write_stringz(out, "R");
        write_stringz(out, args->token);
        write_errno(out, error);
        write_stream(out, '[');
        if (ext != NULL) {
            unsigned i;
            for (i = 0; i < ext->disassemblers_cnt; i++) {
                if (i > 0) write_stream(out, ',');
                write_stream(out, '{');
                json_write_string(out, "ISA");
                write_stream(out, ':');
                json_write_string(out, ext->disassemblers[i].isa);
                write_stream(out, '}');
            }
        }
        write_stream(out, ']');
        write_stream(out, 0);
        write_stream(out, MARKER_EOM);
    }
}
Esempio n. 9
0
static void command_get_capabilities(char * token, Channel * c) {
    int error = 0;
    char id[256];
    Context * ctx = NULL;
    ContextExtensionDS * ext = NULL;
    unsigned i, j;

    json_read_string(&c->inp, id, sizeof(id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    ctx = id2ctx(id);

    if (ctx == NULL) error = ERR_INV_CONTEXT;
    else if (ctx->exited) error = ERR_ALREADY_EXITED;

    ctx = context_get_group(ctx, CONTEXT_GROUP_CPU);
    ext = EXT(ctx);

    write_stringz(&c->out, "R");
    write_stringz(&c->out, token);
    write_errno(&c->out, error);
    write_stream(&c->out, '[');
    for (i = 0, j = 0; i < ext->disassemblers_cnt; i++) {
        if (j > 0) write_stream(&c->out, ',');
        write_stream(&c->out, '{');
        json_write_string(&c->out, "ISA");
        write_stream(&c->out, ':');
        json_write_string(&c->out, ext->disassemblers[i].isa);
        write_stream(&c->out, '}');
        j++;
    }
    write_stream(&c->out, ']');
    write_stream(&c->out, 0);
    write_stream(&c->out, MARKER_EOM);
}
Esempio n. 10
0
static int set_debug_regs(Context * ctx, int * step_over_hw_bp) {
    int i, j;
    ContextAddress pc = 0;
    Context * grp = context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT);
    ContextExtensionARM * ext = EXT(ctx);
    ContextExtensionARM * bps = EXT(grp);
    pid_t pid = id2pid(ctx->id, NULL);

    assert(bps->info_ok);

    ext->armed = 0;
    *step_over_hw_bp = 0;
    if (read_reg(ctx, pc_def, pc_def->size, &pc) < 0) return -1;

    for (i = 0; i < bps->bp_cnt + bps->wp_cnt; i++) {
        uint32_t cr = 0;
        ContextBreakpoint * cb = bps->hw_bps[i];
        if (i == 0 && ext->hw_stepping) {
            uint32_t vr = 0;
            if (ext->hw_stepping == 1) {
                vr = (uint32_t)ext->step_addr & ~0x1;
                cr |= 0x3 << 5;
            }
            else {
                vr = (uint32_t)pc;
                cr |= 0x1 << 22;
                cr |= 0xf << 5;
            }
            cr |= 0x7u;
            if (ptrace(PTRACE_SETHBPREGS, pid, 1, &vr) < 0) return -1;
        }
        else if (cb != NULL) {
            if (i < bps->bp_cnt && ((uint32_t)cb->address & ~0x1) == pc) {
                /* Skipping the breakpoint */
                *step_over_hw_bp = 1;
            }
            else if (bps->arch >= ARM_DEBUG_ARCH_V7_ECP14 && (ext->skip_wp_set & (1u << i))) {
                /* Skipping the watchpoint */
                assert(i >= bps->bp_cnt);
                *step_over_hw_bp = 1;
            }
            else {
                uint32_t vr = (uint32_t)cb->address & ~0x1;
                if (i < bps->bp_cnt) {
                    cr |= 0x3 << 5;
                }
                else {
                    vr = (uint32_t)cb->address & ~0x3;
                    for (j = 0; j < 4; j++) {
                        if (vr + j < cb->address) continue;
                        if (vr + j >= cb->address + cb->length) continue;
                        cr |= 1 << (5 + j);
                    }
                    if (cb->access_types & CTX_BP_ACCESS_DATA_READ) cr |= 1 << 3;
                    if (cb->access_types & CTX_BP_ACCESS_DATA_WRITE) cr |= 1 << 4;
                }
                cr |= 0x7;
                if (i < bps->bp_cnt) {
                    if (ptrace(PTRACE_SETHBPREGS, pid, i * 2 + 1, &vr) < 0) return -1;
                }
                else {
                    if (ptrace(PTRACE_SETHBPREGS, pid, -(i * 2 + 1), &vr) < 0) return -1;
                }
                ext->armed |= 1 << i;
            }
        }
        if (cr == 0) {
            /* Linux kernel does not allow 0 as Control Register value */
            cr |= 0x3u << 1;
            cr |= 0xfu << 5;
            if (i >= bps->bp_cnt) {
                cr |= 1u << 4;
            }
        }
        if (i < bps->bp_cnt) {
            if (ptrace(PTRACE_SETHBPREGS, pid, i * 2 + 2, &cr) < 0) return -1;
        }
        else {
            if (ptrace(PTRACE_SETHBPREGS, pid, -(i * 2 + 2), &cr) < 0) return -1;
        }
    }

    ext->hw_bps_regs_generation = bps->hw_bps_generation;
    return 0;
}
Esempio n. 11
0
static Context * get_reset_context(Context * ctx) {
    ContextExtensionRS * ext = EXT(ctx);
    if (ext->group > 0) return context_get_group(ctx, ext->group);
    return context_get_group(ctx, CONTEXT_GROUP_CPU);
}
Esempio n. 12
0
static void write_context(OutputStream * out, Context * ctx) {
    assert(!ctx->exited);

    write_stream(out, '{');

    json_write_string(out, "ID");
    write_stream(out, ':');
    json_write_string(out, ctx->id);

    if (ctx->parent != NULL) {
        write_stream(out, ',');
        json_write_string(out, "ParentID");
        write_stream(out, ':');
        json_write_string(out, ctx->parent->id);
    }

    write_stream(out, ',');
    json_write_string(out, "ProcessID");
    write_stream(out, ':');
    json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);

    if (ctx->name != NULL) {
        write_stream(out, ',');
        json_write_string(out, "Name");
        write_stream(out, ':');
        json_write_string(out, ctx->name);
    }

    write_stream(out, ',');
    json_write_string(out, "BigEndian");
    write_stream(out, ':');
    json_write_boolean(out, ctx->big_endian);

    if (ctx->mem_access) {
        int cnt = 0;

        write_stream(out, ',');
        json_write_string(out, "AddressSize");
        write_stream(out, ':');
        json_write_ulong(out, context_word_size(ctx));

        write_stream(out, ',');
        json_write_string(out, "AccessTypes");
        write_stream(out, ':');
        write_stream(out, '[');
        if (ctx->mem_access & MEM_ACCESS_INSTRUCTION) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "instruction");
        }
        if (ctx->mem_access & MEM_ACCESS_DATA) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "data");
        }
        if (ctx->mem_access & MEM_ACCESS_IO) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "io");
        }
        if (ctx->mem_access & MEM_ACCESS_USER) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "user");
        }
        if (ctx->mem_access & MEM_ACCESS_SUPERVISOR) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "supervisor");
        }
        if (ctx->mem_access & MEM_ACCESS_HYPERVISOR) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "hypervisor");
        }
        if (ctx->mem_access & MEM_ACCESS_VIRTUAL) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "virtual");
        }
        if (ctx->mem_access & MEM_ACCESS_PHYSICAL) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "physical");
        }
        if (ctx->mem_access & MEM_ACCESS_CACHE) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "cache");
        }
        if (ctx->mem_access & MEM_ACCESS_TLB) {
            if (cnt++) write_stream(out, ',');
            json_write_string(out, "tlb");
        }
        write_stream(out, ']');
    }

    write_stream(out, '}');
}
Esempio n. 13
0
static void write_context(OutputStream * out, char * id,
        Context * ctx, int frame, RegisterDefinition * reg_def) {

    assert(!ctx->exited);

    write_stream(out, '{');

    json_write_string(out, "ID");
    write_stream(out, ':');
    json_write_string(out, id);

    write_stream(out, ',');
    json_write_string(out, "ParentID");
    write_stream(out, ':');
    if (reg_def->parent != NULL) {
        json_write_string(out, register2id(ctx, frame, reg_def->parent));
    }
    else if (frame < 0 || is_top_frame(ctx, frame)) {
        json_write_string(out, ctx->id);
    }
    else {
        json_write_string(out, frame2id(ctx, frame));
    }

    write_stream(out, ',');
    json_write_string(out, "ProcessID");
    write_stream(out, ':');
    json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);

    write_stream(out, ',');
    json_write_string(out, "Name");
    write_stream(out, ':');
    json_write_string(out, reg_def->name);

    if (reg_def->size > 0) {
        write_stream(out, ',');
        json_write_string(out, "Size");
        write_stream(out, ':');
        json_write_long(out, reg_def->size);
    }

    if (reg_def->dwarf_id >= 0) {
        write_stream(out, ',');
        json_write_string(out, "DwarfID");
        write_stream(out, ':');
        json_write_long(out, reg_def->dwarf_id);
    }

    if (reg_def->eh_frame_id >= 0) {
        write_stream(out, ',');
        json_write_string(out, "EhFrameID");
        write_stream(out, ':');
        json_write_long(out, reg_def->eh_frame_id);
    }

    write_boolean_member(out, "BigEndian", reg_def->big_endian);
    write_boolean_member(out, "Float", reg_def->fp_value);
    write_boolean_member(out, "Readable", !reg_def->no_read);
    write_boolean_member(out, "Writeable", !reg_def->no_write);
    write_boolean_member(out, "ReadOnce", reg_def->read_once);
    write_boolean_member(out, "WriteOnce", reg_def->write_once);
    write_boolean_member(out, "Volatile", reg_def->volatile_value);
    write_boolean_member(out, "SideEffects", reg_def->side_effects);
    write_boolean_member(out, "LeftToRight", reg_def->left_to_right);

    if (reg_def->first_bit > 0) {
        write_stream(out, ',');
        json_write_string(out, "FirstBit");
        write_stream(out, ':');
        json_write_long(out, reg_def->first_bit);
    }

    if (reg_def->bits != NULL) {
        int i = 0;
        write_stream(out, ',');
        json_write_string(out, "Bits");
        write_stream(out, ':');
        write_stream(out, '[');
        while (reg_def->bits[i] >= 0) {
            if (i > 0) write_stream(out, ',');
            json_write_long(out, reg_def->bits[i++]);
        }
        write_stream(out, ']');
    }

    if (reg_def->values != NULL) {
        int i = 0;
        write_stream(out, ',');
        json_write_string(out, "Values");
        write_stream(out, ':');
        write_stream(out, '[');
        while (reg_def->values[i] != NULL) {
            NamedRegisterValue * v = reg_def->values[i++];
            if (i > 1) write_stream(out, ',');
            write_stream(out, '{');
            json_write_string(out, "Value");
            write_stream(out, ':');
            json_write_binary(out, v->value, reg_def->size);
            if (v->name != NULL) {
                write_stream(out, ',');
                json_write_string(out, "Name");
                write_stream(out, ':');
                json_write_string(out, v->name);
            }
            if (v->description != NULL) {
                write_stream(out, ',');
                json_write_string(out, "Description");
                write_stream(out, ':');
                json_write_string(out, v->description);
            }
            write_stream(out, '}');
        }
        write_stream(out, ']');
    }

    if (reg_def->memory_address > 0) {
        write_stream(out, ',');
        json_write_string(out, "MemoryAddress");
        write_stream(out, ':');
        json_write_uint64(out, reg_def->memory_address);
    }

    if (reg_def->memory_context != NULL) {
        write_stream(out, ',');
        json_write_string(out, "MemoryContext");
        write_stream(out, ':');
        json_write_string(out, reg_def->memory_context);
    }

    if (reg_def->role != NULL) {
        write_stream(out, ',');
        json_write_string(out, "Role");
        write_stream(out, ':');
        json_write_string(out, reg_def->role);
    }
    else if (reg_def == get_PC_definition(ctx)) {
        write_stream(out, ',');
        json_write_string(out, "Role");
        write_stream(out, ':');
        json_write_string(out, "PC");
    }

    if (reg_def->description != NULL) {
        write_stream(out, ',');
        json_write_string(out, "Description");
        write_stream(out, ':');
        json_write_string(out, reg_def->description);
    }

    if (reg_def->size > 0) {
        RegisterDefinition * parent_reg_def = NULL;
        parent_reg_def = reg_def->parent;
        while (parent_reg_def != NULL && parent_reg_def->size == 0) parent_reg_def = parent_reg_def->parent;
        if (parent_reg_def != NULL) {
            if (reg_def->offset >= parent_reg_def->offset &&
                reg_def->offset + reg_def->size <= parent_reg_def->offset + parent_reg_def->size) {
                write_stream(out, ',');
                json_write_string(out, "Offset");
                write_stream(out, ':');
                json_write_uint64(out, reg_def->offset - parent_reg_def->offset);
            }
        }
    }

    write_stream(out, '}');
    write_stream(out, 0);
}
Esempio n. 14
0
static int disassemble_block(Context * ctx, OutputStream * out, uint8_t * mem_buf,
                              ContextAddress buf_addr, ContextAddress buf_size,
                              ContextAddress mem_size, ContextISA * isa,
                              DisassembleCmdArgs * args) {
    ContextAddress offs = 0;
    Disassembler * disassembler = NULL;
    Context * cpu = context_get_group(ctx, CONTEXT_GROUP_CPU);
    int disassembler_ok = 0;
    DisassemblerParams param;

    param.ctx = ctx;
    param.big_endian = ctx->big_endian;
    param.pseudo_instr = args->pseudo_instr;
    param.simplified = args->simplified;
    if (args->isa) {
        isa->isa = args->isa;
        isa->addr = args->addr;
        isa->size = args->size;
    }

    write_stream(out, '[');
    while (offs < buf_size && offs < mem_size) {
        ContextAddress addr = buf_addr + offs;
        ContextAddress size = mem_size - offs;
        DisassemblyResult * dr = NULL;
        if (args->isa == NULL && (addr < isa->addr ||
                (isa->addr + isa->size >= isa->addr && addr >= isa->addr + isa->size))) {
            if (get_isa(ctx, addr, isa) < 0) return -1;
            disassembler_ok = 0;
        }
        if (!disassembler_ok) {
            disassembler = find_disassembler(cpu, isa->isa);
            if (disassembler == NULL) disassembler = find_disassembler(cpu, isa->def);
            disassembler_ok = 1;
        }
        if (disassembler) dr = disassembler(mem_buf + (size_t)offs, addr, size, &param);
        if (dr == NULL) {
            static char buf[32];
            static DisassemblyResult dd;
            memset(&dd, 0, sizeof(dd));
            if (isa->alignment >= 4 && (addr & 0x3) == 0 && offs <= mem_size + 4) {
                unsigned i;
                uint32_t v = 0;
                for (i = 0; i < 4; i++) v |= (uint32_t)mem_buf[offs + i] << (i * 8);
                snprintf(buf, sizeof(buf), ".word 0x%08x", v);
                dd.size = 4;
            }
            else if (isa->alignment >= 2 && (addr & 0x1) == 0 && offs <= mem_size + 2) {
                unsigned i;
                uint16_t v = 0;
                for (i = 0; i < 2; i++) v |= (uint16_t)mem_buf[offs + i] << (i * 8);
                snprintf(buf, sizeof(buf), ".half 0x%04x", v);
                dd.size = 2;
            }
            else {
                snprintf(buf, sizeof(buf), ".byte 0x%02x", mem_buf[offs]);
                dd.size = 1;
            }
            dd.text = buf;
            dr = &dd;
        }
        assert(dr->size > 0);
        if (offs > 0) write_stream(out, ',');
        write_stream(out, '{');
        json_write_string(out, "Address");
        write_stream(out, ':');
        json_write_uint64(out, addr);
        write_stream(out, ',');
        json_write_string(out, "Size");
        write_stream(out, ':');
        json_write_uint64(out, dr->size);
        write_stream(out, ',');
        json_write_string(out, "Instruction");
        write_stream(out, ':');
        write_stream(out, '[');
        write_stream(out, '{');
        json_write_string(out, "Type");
        write_stream(out, ':');
        json_write_string(out, "String");
        write_stream(out, ',');
        json_write_string(out, "Text");
        write_stream(out, ':');
        json_write_string(out, dr->text);
        write_stream(out, '}');
        write_stream(out, ']');
        if (args->opcode_value) {
            write_stream(out, ',');
            json_write_string(out, "OpcodeValue");
            write_stream(out, ':');
            json_write_binary(out, mem_buf + (size_t)offs, (size_t)dr->size);
        }
        write_stream(out, '}');
        offs += dr->size;
    }
    write_stream(out, ']');
    return 0;
}