static void validate_lex_operand(Validator *val, MVMuint32 flags) { MVMuint16 lex_index, frame_index, i; MVMuint32 lex_count; MVMStaticFrame *frame = val->frame; /* Two steps forward, two steps back to keep the error reporting happy, and to make the endian conversion within ensure_bytes correct. (Both are using val->cur_op, and want it to have different values.) */ ensure_bytes(val, 2); lex_index = GET_UI16(val->cur_op, 0); val->cur_op += 2; ensure_bytes(val, 2); val->cur_op -= 2; frame_index = GET_UI16(val->cur_op, 2); for (i = frame_index; i; i--) { frame = frame->body.outer; if (!frame) fail(val, MSG(val, "lexical operand requires %" PRIu16 " more enclosing scopes"), i); } lex_count = frame->body.num_lexicals; if (lex_index >= lex_count) fail(val, MSG(val, "lexical operand index %" PRIu16 " out of range 0.. %" PRIu32), lex_index, lex_count - 1); val->cur_op += 4; }
void check_free_entry(byte *sp, byte *ep, byte tag, Uint16 ct_no, int ct_no_n, Uint16 t_no, int t_no_n, UWord ptr, int ptr_n, Uint32 ti,int ti_n) { byte *p = sp; Uint16 hdr; ASSERT(*p == tag); p++; hdr = GET_UI16(p); if (tag == ERTS_MT_CRR_FREE_BDY_TAG) check_ui(&hdr, &p, ct_no, ct_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ); check_ui(&hdr, &p, t_no, t_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ); check_ui(&hdr, &p, ptr, ptr_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ); check_ui(&hdr, &p, ti, ti_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ); ASSERT(hdr == 0); ASSERT(p == ep); }
void check_alloc_entry(byte *sp, byte *ep, byte tag, Uint16 ct_no, int ct_no_n, Uint16 t_no, int t_no_n, UWord res, int res_n, Uint size, int size_n, Uint32 ti,int ti_n) { byte *p = sp; Uint16 hdr; ASSERT(*p == tag); p++; hdr = GET_UI16(p); if (tag == ERTS_MT_CRR_ALLOC_BDY_TAG) check_ui(&hdr, &p, ct_no, ct_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ); check_ui(&hdr, &p, t_no, t_no_n, UI16_MSB_EHF_MSK, UI16_MSB_EHF_SZ); check_ui(&hdr, &p, res, res_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ); check_ui(&hdr, &p, size, size_n, UI_MSB_EHF_MSK, UI_MSB_EHF_SZ); check_ui(&hdr, &p, ti, ti_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ); ASSERT(hdr == 0); ASSERT(p == ep); }
static void validate_block(Validator *val) { int block_id = val->cur_mark[1]; switch (block_id) { case 'a': { MVMuint16 index; ensure_op(val, MVM_OP_prepargs); validate_operands(val); index = GET_UI16(val->cur_op, -2); val->cur_call = val->cu->body.callsites[index]; val->cur_arg = 0; val->expected_named_arg = 0; val->remaining_positionals = val->cur_call->num_pos; break; } default: fail(val, MSG(val, "unknown instruction block '%c'"), block_id); } while (val->cur_op < val->bc_end) { int type, id; read_op(val); type = val->cur_mark[0]; id = val->cur_mark[1]; if (id != block_id) fail(val, MSG(val, "expected instruction marked '%c' but got '%c'"), block_id, id); switch (type) { case MARK_body: break; case MARK_tail: goto terminate_block; default: fail_illegal_mark(val); } switch (block_id) { case 'a': validate_operands(val); validate_arg(val); break; } } terminate_block: switch (block_id) { case 'a': validate_operands(val); ensure_no_remaining_positionals(val); break; } }
void check_time_inc_entry(byte *sp, byte *ep, Uint32 secs, int secs_n, Uint32 usecs, int usecs_n) { byte *p = sp; Uint16 hdr; ASSERT(*p == ERTS_MT_TIME_INC_BDY_TAG); p++; hdr = GET_UI16(p); check_ui(&hdr, &p, secs, secs_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ); check_ui(&hdr, &p, usecs, usecs_n, UI32_MSB_EHF_MSK, UI32_MSB_EHF_SZ); ASSERT(hdr == 0); ASSERT(p == ep); }
static void validate_literal_operand(Validator *val, MVMuint32 flags) { MVMuint32 type = flags & MVM_operand_type_mask; MVMuint32 size; switch (type) { case MVM_operand_int8: size = 1; break; case MVM_operand_int16: size = 2; break; case MVM_operand_int32: size = 4; break; case MVM_operand_int64: size = 8; break; case MVM_operand_num32: size = 4; break; case MVM_operand_num64: size = 8; break; case MVM_operand_callsite: size = 2; break; case MVM_operand_coderef: size = 2; break; case MVM_operand_str: size = 4; break; case MVM_operand_ins: size = 4; break; case MVM_operand_obj: case MVM_operand_type_var: fail(val, MSG(val, "operand type %i can't be a literal"), type); default: fail(val, MSG(val, "unknown operand type %i"), type); } ensure_bytes(val, size); switch (type) { case MVM_operand_callsite: { MVMuint16 index = GET_UI16(val->cur_op, 0); MVMuint32 count = val->cu->body.orig_callsites; if (index >= count) fail(val, MSG(val, "callsite index %" PRIu16 " out of range 0..%" PRIu32), index, count - 1); break; } case MVM_operand_coderef: { MVMuint16 index = GET_UI16(val->cur_op, 0); MVMuint32 count = val->cu->body.orig_frames; if (index >= count) fail(val, MSG(val, "coderef index %" PRIu16 " out of range 0..%" PRIu32), index, count - 1); break; } case MVM_operand_str: { MVMuint32 index = GET_UI32(val->cur_op, 0); MVMuint32 count = val->cu->body.orig_strings; if (index >= count) fail(val, MSG(val, "string index %" PRIu32 " out of range 0..%" PRIu32), index, count - 1); break; } case MVM_operand_ins: { MVMuint32 offset = GET_UI32(val->cur_op, 0); if (offset >= val->bc_size) fail(val, MSG(val, "branch instruction offset %" PRIu32 " out of range 0..%" PRIu32), offset, val->bc_size - 1); val->labels[offset] |= MVM_BC_branch_target; } } val->cur_op += size; }
static void build_cfg(MVMThreadContext *tc, MVMSpeshGraph *g, MVMStaticFrame *sf, MVMint32 *existing_deopts, MVMint32 num_existing_deopts) { MVMSpeshBB *cur_bb, *prev_bb; MVMSpeshIns *last_ins; MVMint64 i; MVMint32 bb_idx; /* Temporary array of all MVMSpeshIns we create (one per instruction). * Overestimate at size. Has the flat view, matching the bytecode. */ MVMSpeshIns **ins_flat = MVM_calloc(g->bytecode_size / 2, sizeof(MVMSpeshIns *)); /* Temporary array where each byte in the input bytecode gets a 32-bit * integer. This is used for two things: * A) When we make the MVMSpeshIns for an instruction starting at the * byte, we put the instruction index (into ins_flat) in the slot, * shifting it by 2 bits to the left. We will use this to do fixups. * B) The first bit is "I have an incoming branch" - that is, start of * a basic block. The second bit is "I can branch" - that is, end of * a basic block. It's possible to have both bits set. * Anything that's just a zero has no instruction starting there. */ MVMuint32 *byte_to_ins_flags = MVM_calloc(g->bytecode_size, sizeof(MVMuint32)); /* Instruction to basic block mapping. Initialized later. */ MVMSpeshBB **ins_to_bb = NULL; /* Make first pass through the bytecode. In this pass, we make MVMSpeshIns * nodes for each instruction and set the start/end of block bits. Also * set handler targets as basic block starters. */ MVMCompUnit *cu = sf->body.cu; MVMuint8 *pc = g->bytecode; MVMuint8 *end = g->bytecode + g->bytecode_size; MVMuint32 ins_idx = 0; MVMuint8 next_bbs = 1; /* Next iteration (here, first) starts a BB. */ for (i = 0; i < g->num_handlers; i++) byte_to_ins_flags[g->handlers[i].goto_offset] |= MVM_CFG_BB_START; while (pc < end) { /* Look up op info. */ MVMuint16 opcode = *(MVMuint16 *)pc; MVMuint8 *args = pc + 2; MVMuint8 arg_size = 0; const MVMOpInfo *info = get_op_info(tc, cu, opcode); /* Create an instruction node, add it, and record its position. */ MVMSpeshIns *ins_node = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); ins_flat[ins_idx] = ins_node; byte_to_ins_flags[pc - g->bytecode] |= ins_idx << 2; /* Did previous instruction end a basic block? */ if (next_bbs) { byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_START; next_bbs = 0; } /* Also check we're not already a BB start due to being a branch * target, in which case we should ensure our prior is marked as * a BB end. */ else { if (byte_to_ins_flags[pc - g->bytecode] & MVM_CFG_BB_START) { MVMuint32 hunt = pc - g->bytecode; while (!byte_to_ins_flags[--hunt]); byte_to_ins_flags[hunt] |= MVM_CFG_BB_END; } } /* Store opcode */ ins_node->info = info; /* Go over operands. */ ins_node->operands = MVM_spesh_alloc(tc, g, info->num_operands * sizeof(MVMSpeshOperand)); for (i = 0; i < info->num_operands; i++) { MVMuint8 flags = info->operands[i]; MVMuint8 rw = flags & MVM_operand_rw_mask; switch (rw) { case MVM_operand_read_reg: case MVM_operand_write_reg: ins_node->operands[i].reg.orig = GET_UI16(args, arg_size); arg_size += 2; break; case MVM_operand_read_lex: case MVM_operand_write_lex: ins_node->operands[i].lex.idx = GET_UI16(args, arg_size); ins_node->operands[i].lex.outers = GET_UI16(args, arg_size + 2); arg_size += 4; break; case MVM_operand_literal: { MVMuint32 type = flags & MVM_operand_type_mask; switch (type) { case MVM_operand_int8: ins_node->operands[i].lit_i8 = GET_I8(args, arg_size); arg_size += 1; break; case MVM_operand_int16: ins_node->operands[i].lit_i16 = GET_I16(args, arg_size); arg_size += 2; break; case MVM_operand_int32: ins_node->operands[i].lit_i32 = GET_I32(args, arg_size); arg_size += 4; break; case MVM_operand_int64: ins_node->operands[i].lit_i64 = MVM_BC_get_I64(args, arg_size); arg_size += 8; break; case MVM_operand_num32: ins_node->operands[i].lit_n32 = GET_N32(args, arg_size); arg_size += 4; break; case MVM_operand_num64: ins_node->operands[i].lit_n64 = MVM_BC_get_N64(args, arg_size); arg_size += 8; break; case MVM_operand_callsite: ins_node->operands[i].callsite_idx = GET_UI16(args, arg_size); arg_size += 2; break; case MVM_operand_coderef: ins_node->operands[i].coderef_idx = GET_UI16(args, arg_size); arg_size += 2; break; case MVM_operand_str: ins_node->operands[i].lit_str_idx = GET_UI32(args, arg_size); arg_size += 4; break; case MVM_operand_ins: { /* Stash instruction offset. */ MVMuint32 target = GET_UI32(args, arg_size); ins_node->operands[i].ins_offset = target; /* This is a branching instruction, so it's a BB end. */ byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END; /* Its target is a BB start, and any previous instruction * we already passed needs marking as a BB end. */ byte_to_ins_flags[target] |= MVM_CFG_BB_START; if (target > 0 && target < pc - g->bytecode) { while (!byte_to_ins_flags[--target]); byte_to_ins_flags[target] |= MVM_CFG_BB_END; } /* Next instruction is also a BB start. */ next_bbs = 1; arg_size += 4; break; } case MVM_operand_spesh_slot: ins_node->operands[i].lit_i16 = GET_I16(args, arg_size); arg_size += 2; break; default: MVM_exception_throw_adhoc(tc, "Spesh: unknown operand type %d in graph building (op %s)", (int)type, ins_node->info->name); } } break; } } /* We specially handle the jumplist case, which needs to mark all of * the possible places we could jump to in the following instructions * as starts of basic blocks. It is, in itself, the end of one. Note * we jump to the instruction after the n jump points if none match, * so that is marked too. */ if (opcode == MVM_OP_jumplist) { MVMint64 n = MVM_BC_get_I64(args, 0); for (i = 0; i <= n; i++) byte_to_ins_flags[(pc - g->bytecode) + 12 + i * 6] |= MVM_CFG_BB_START; byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END; } /* Invocations, returns, and throws are basic block ends. */ switch (opcode) { case MVM_OP_invoke_v: case MVM_OP_invoke_i: case MVM_OP_invoke_n: case MVM_OP_invoke_s: case MVM_OP_invoke_o: case MVM_OP_return_i: case MVM_OP_return_n: case MVM_OP_return_s: case MVM_OP_return_o: case MVM_OP_return: case MVM_OP_throwdyn: case MVM_OP_throwlex: case MVM_OP_throwlexotic: case MVM_OP_throwcatdyn: case MVM_OP_throwcatlex: case MVM_OP_throwcatlexotic: case MVM_OP_die: case MVM_OP_rethrow: case MVM_OP_resume: byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END; next_bbs = 1; break; } /* Final instruction is basic block end. */ if (pc + 2 + arg_size == end) byte_to_ins_flags[pc - g->bytecode] |= MVM_CFG_BB_END; /* Caculate next instruction's PC. */ pc += 2 + arg_size; /* If this is a deopt point opcode... */ if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_ONE)) add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_ONE_INS); if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_ALL)) add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_ALL_INS); if (!existing_deopts && (info->deopt_point & MVM_DEOPT_MARK_OSR)) add_deopt_annotation(tc, g, ins_node, pc, MVM_SPESH_ANN_DEOPT_OSR); /* Go to next instruction. */ ins_idx++; } /* Annotate instructions that are handler-significant. */ for (i = 0; i < g->num_handlers; i++) { MVMSpeshIns *start_ins = ins_flat[byte_to_ins_flags[g->handlers[i].start_offset] >> 2]; MVMSpeshIns *end_ins = ins_flat[byte_to_ins_flags[g->handlers[i].end_offset] >> 2]; MVMSpeshIns *goto_ins = ins_flat[byte_to_ins_flags[g->handlers[i].goto_offset] >> 2]; MVMSpeshAnn *start_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn)); MVMSpeshAnn *end_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn)); MVMSpeshAnn *goto_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn)); start_ann->next = start_ins->annotations; start_ann->type = MVM_SPESH_ANN_FH_START; start_ann->data.frame_handler_index = i; start_ins->annotations = start_ann; end_ann->next = end_ins->annotations; end_ann->type = MVM_SPESH_ANN_FH_END; end_ann->data.frame_handler_index = i; end_ins->annotations = end_ann; goto_ann->next = goto_ins->annotations; goto_ann->type = MVM_SPESH_ANN_FH_GOTO; goto_ann->data.frame_handler_index = i; goto_ins->annotations = goto_ann; } /* Annotate instructions that are inline start/end points. */ for (i = 0; i < g->num_inlines; i++) { MVMSpeshIns *start_ins = ins_flat[byte_to_ins_flags[g->inlines[i].start] >> 2]; MVMSpeshIns *end_ins = ins_flat[byte_to_ins_flags[g->inlines[i].end] >> 2]; MVMSpeshAnn *start_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn)); MVMSpeshAnn *end_ann = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshAnn)); start_ann->next = start_ins->annotations; start_ann->type = MVM_SPESH_ANN_INLINE_START; start_ann->data.inline_idx = i; start_ins->annotations = start_ann; end_ann->next = end_ins->annotations; end_ann->type = MVM_SPESH_ANN_INLINE_END; end_ann->data.inline_idx = i; end_ins->annotations = end_ann; } /* Now for the second pass, where we assemble the basic blocks. Also we * build a lookup table of instructions that start a basic block to that * basic block, for the final CFG construction. We make the entry block a * special one, containing a noop; it will have any exception handler * targets linked from it, so they show up in the graph. */ g->entry = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB)); g->entry->first_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); g->entry->first_ins->info = get_op_info(tc, cu, 0); g->entry->last_ins = g->entry->first_ins; g->entry->idx = 0; cur_bb = NULL; prev_bb = g->entry; last_ins = NULL; ins_to_bb = MVM_calloc(ins_idx, sizeof(MVMSpeshBB *)); ins_idx = 0; bb_idx = 1; for (i = 0; i < g->bytecode_size; i++) { MVMSpeshIns *cur_ins; /* Skip zeros; no instruction here. */ if (!byte_to_ins_flags[i]) continue; /* Get current instruction. */ cur_ins = ins_flat[byte_to_ins_flags[i] >> 2]; /* Start of a basic block? */ if (byte_to_ins_flags[i] & MVM_CFG_BB_START) { /* Should not already be in a basic block. */ if (cur_bb) { MVM_spesh_graph_destroy(tc, g); MVM_exception_throw_adhoc(tc, "Spesh: confused during basic block analysis (in block)"); } /* Create it, and set first instruction and index. */ cur_bb = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshBB)); cur_bb->first_ins = cur_ins; cur_bb->idx = bb_idx; bb_idx++; /* Record instruction -> BB start mapping. */ ins_to_bb[ins_idx] = cur_bb; /* Link it to the previous one. */ prev_bb->linear_next = cur_bb; } /* Should always be in a BB at this point. */ if (!cur_bb) { MVM_spesh_graph_destroy(tc, g); MVM_exception_throw_adhoc(tc, "Spesh: confused during basic block analysis (no block)"); } /* Add instruction into double-linked per-block instruction list. */ if (last_ins) { last_ins->next = cur_ins; cur_ins->prev = last_ins; } last_ins = cur_ins; /* End of a basic block? */ if (byte_to_ins_flags[i] & MVM_CFG_BB_END) { cur_bb->last_ins = cur_ins; prev_bb = cur_bb; cur_bb = NULL; last_ins = NULL; } ins_idx++; } g->num_bbs = bb_idx; /* Finally, link the basic blocks up to form a CFG. Along the way, any of * the instruction operands get the target BB stored. */ cur_bb = g->entry; while (cur_bb) { /* If it's the first block, it's a special case; successors are the * real successor and all exception handlers. */ if (cur_bb == g->entry) { cur_bb->num_succ = 1 + g->num_handlers; cur_bb->succ = MVM_spesh_alloc(tc, g, cur_bb->num_succ * sizeof(MVMSpeshBB *)); cur_bb->succ[0] = cur_bb->linear_next; for (i = 0; i < g->num_handlers; i++) { MVMuint32 offset = g->handlers[i].goto_offset; cur_bb->succ[i + 1] = ins_to_bb[byte_to_ins_flags[offset] >> 2]; } } /* Otherwise, consider the last instruction, to see how we leave the BB. */ else { switch (cur_bb->last_ins->info->opcode) {
int unZ(char *archiveFilename, char *extractPath) { FILE *fp; fchunk_t fc; unsigned int i, destsize; unsigned char header[256], *toc; unsigned int numFiles, compsize, tocOffset, tocSize, numDirs; direntry_t *dirs; destsize = strlen(extractPath); fp = fopen(archiveFilename, "rb"); if (!fp) { printf("Error opening %s!\n", archiveFilename); return; } fseek(fp, 0, SEEK_END); compsize = ftell(fp); fseek(fp, 0, SEEK_SET); fread(header, 256, 1, fp); // check signature if (GET_UI32(header) != 0x8C655D13) { printf("Archive corrupt: Bad signature!\n"); return 0; } //numFiles = GET_UI16(header + 12); // check size if (compsize != GET_UI32(header + 18)) { printf("Archive corrupt: Incorrect size!\n"); return 0; } // read toc tocOffset = GET_UI32(header + 41); tocSize = compsize - tocOffset; toc = malloc(tocSize); fseek(fp, tocOffset, SEEK_SET); fread(toc, 1, tocSize, fp); tocOffset = 0; // parse dirs numDirs = GET_UI16(header + 49); dirs = malloc(sizeof(*dirs) * numDirs); for (i = 0; i < numDirs; i++) { unsigned int dir_numFiles = GET_UI16(toc + tocOffset); unsigned int dir_entrySize = GET_UI16(toc + tocOffset + 2); unsigned int dir_nameSize = GET_UI16(toc + tocOffset + 4); dirs[i].name = malloc(destsize + 1 + dir_nameSize + 1); strcpy(dirs[i].name, extractPath); dirs[i].name[destsize] = '\\'; memcpy(dirs[i].name + destsize + 1, toc + tocOffset + 6, dir_nameSize); dirs[i].name[destsize + 1 + dir_nameSize] = '\0'; dirs[i].numFiles = dir_numFiles; tocOffset += dir_entrySize; } // parse and save files fseek(fp, 255, SEEK_SET); fc.fp = fp; for (i = 0; i < numDirs; i++) { unsigned int dir_nameSize; int j; _mkdir(extractPath); if (dirs[i].name[0]) _mkdir(dirs[i].name); printf("Dir %s, %d files\n", dirs[i].name, dirs[i].numFiles); dir_nameSize = strlen(dirs[i].name); for (j = 0; j < dirs[i].numFiles; j++) { FILE *fpw; int blastErr; unsigned int file_decompressedSize = GET_UI32(toc + tocOffset + 3); unsigned int file_compressedSize = GET_UI32(toc + tocOffset + 7); unsigned int file_modifiedDate = GET_UI16(toc + tocOffset + 15); unsigned int file_modifiedTime = GET_UI16(toc + tocOffset + 17); unsigned int file_entrySize = GET_UI16(toc + tocOffset + 23); unsigned int file_nameSize = toc[tocOffset + 29]; char *filename; struct tm tmm; struct _utimbuf ut; filename = malloc(dir_nameSize + file_nameSize + 2); strcpy(filename, dirs[i].name); filename[dir_nameSize] = '\\'; strncpy(filename + dir_nameSize + 1, toc + tocOffset + 30, file_nameSize); filename[dir_nameSize + 1 + file_nameSize] = '\0'; printf("%s, %d bytes, %.3f%%\n", filename, file_decompressedSize, (float)file_compressedSize / file_decompressedSize * 100.0f); memset(&tmm, 0, sizeof(tmm)); tmm.tm_year = ( file_modifiedDate >> 9) + 80; tmm.tm_mon = ((file_modifiedDate & 0x01E0) >> 5) - 1; tmm.tm_mday = file_modifiedDate & 0x001F; tmm.tm_hour = file_modifiedTime >> 11; tmm.tm_min = (file_modifiedTime & 0x07E0) >> 5; tmm.tm_sec = (file_modifiedTime & 0x001F) * 2; #if defined(SHOWTIME) printf("last modified %d %s %d %02d:%02d:%02d\n", tmm.tm_year + 1900, months[tmm.tm_mon], tmm.tm_mday, tmm.tm_hour, tmm.tm_min, tmm.tm_sec); #endif tocOffset += file_entrySize; fpw = fopen(filename, "wb"); fc.size = file_compressedSize; blastErr = blast(inf, &fc, outf, fpw); if (blastErr == 1) printf("Error writing file!\n"); else if (blastErr != 0) printf("Archive corrupt: Error decompressing, trying to continue"); fclose(fpw); ut.actime = time(NULL); ut.modtime = mktime(&tmm); _utime(filename, &ut); free(filename); } } fclose(fp); for (i = 0; i < numDirs; i++) free(dirs[i].name); free(dirs); free(toc); return 0; }
/* Validate that a static frame's bytecode is executable by the interpreter */ void MVM_validate_static_frame(MVMThreadContext *tc, MVMStaticFrame *static_frame) { MVMCompUnit *cu = static_frame->cu; MVMuint32 bytecode_size = static_frame->bytecode_size; MVMuint8 *bytecode_start = static_frame->bytecode; MVMuint8 *bytecode_end = bytecode_start + bytecode_size; /* current position in the bytestream */ MVMuint8 *cur_op = bytecode_start; /* positions in the bytestream that are starts of ops and goto targets */ MVMuint8 *labels = malloc(bytecode_size); MVMuint32 num_locals = static_frame->num_locals; MVMuint32 branch_target; MVMuint8 bank_num; MVMuint8 op_num; MVMOpInfo *op_info; MVMuint32 operand_size; MVMuint16 operand_target; MVMuint32 instruction = 0; MVMuint32 i; unsigned char op_rw; unsigned char op_type; unsigned char op_flags; MVMuint32 operand_type_var; MVMint64 num_jumplist_labels = 0; memset(labels, 0, bytecode_size); /* printf("bytecode_size %d cur_op %d bytecode_end %d difference %d", bytecode_size, (int)cur_op, (int)bytecode_end, (int)(bytecode_end - cur_op)); */ while (cur_op < bytecode_end - 1) { labels[cur_op - bytecode_start] |= MVM_val_op_boundary; bank_num = *(cur_op++); op_num = *(cur_op++); operand_type_var = 0; op_info = MVM_op_get_op((unsigned char)bank_num, (unsigned char)op_num); if (!op_info) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: non-existent operation bank %u op %u", bank_num, op_num); } if (num_jumplist_labels != 0 && num_jumplist_labels-- != 0 && (bank_num != MVM_OP_BANK_primitives || op_num != MVM_OP_goto)) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "jumplist op must be followed by an additional %d goto ops", num_jumplist_labels + 1); } /*printf("validating op %s, (%d) bank %d", op_info->name, op_num, bank_num);*/ for (i = 0; i < op_info->num_operands; i++) { op_flags = op_info->operands[i]; op_rw = op_flags & MVM_operand_rw_mask; op_type = op_flags & MVM_operand_type_mask; if (op_rw == MVM_operand_literal) { switch (op_type) { case MVM_operand_int8: operand_size = 1; break; case MVM_operand_int16: operand_size = 2; break; case MVM_operand_int32: operand_size = 4; break; case MVM_operand_int64: operand_size = 8; if (bank_num == MVM_OP_BANK_primitives && op_num == MVM_OP_jumplist) { if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); num_jumplist_labels = GET_I64(cur_op, 0); if (num_jumplist_labels < 0 || num_jumplist_labels > 4294967295u) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "num_jumplist_labels %d out of range", num_jumplist_labels); } } break; case MVM_operand_num32: operand_size = 4; break; case MVM_operand_num64: operand_size = 8; break; case MVM_operand_callsite: operand_size = 2; if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); operand_target = GET_UI16(cur_op, 0); if (operand_target >= cu->num_callsites) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: callsites index (%u) out of range; frame has %u callsites", operand_target, cu->num_callsites); } break; case MVM_operand_coderef: operand_size = 2; if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); operand_target = GET_UI16(cur_op, 0); if (operand_target >= cu->num_frames) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: coderef index (%u) out of range; frame has %u coderefs", operand_target, cu->num_frames); } break; /* reset to 0 */ case MVM_operand_str: operand_size = 2; if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); operand_target = GET_UI16(cur_op, 0); if (operand_target >= cu->num_strings) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: strings index (%u) out of range (0-%u)", operand_target, cu->num_strings - 1); } break; case MVM_operand_ins: operand_size = 4; if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); branch_target = GET_UI32(cur_op, 0); if (branch_target >= bytecode_size) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: branch instruction offset (%u) out of range; frame has %u bytes", branch_target, bytecode_size); } labels[branch_target] |= MVM_val_branch_target; break; case MVM_operand_obj: case MVM_operand_type_var: cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: that operand type (%u) can't be a literal", (MVMuint8)op_type); break; default: { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: non-existent operand type (%u)", (MVMuint8)op_type); } } if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); } else if (op_rw == MVM_operand_read_reg || op_rw == MVM_operand_write_reg) { /* register operand */ operand_size = 2; if (cur_op + operand_size > bytecode_end) throw_past_end(tc, labels); if (GET_REG(cur_op, 0) >= num_locals) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: operand register index (%u) out of range; frame has %u locals; at byte %u", GET_REG(cur_op, 0), num_locals, cur_op - bytecode_start); } if (op_type == MVM_operand_type_var) { if (operand_type_var) { /* XXX assume only one type variable */ if ((static_frame->local_types[GET_REG(cur_op, 0)] << 3) != operand_type_var) { cleanup_all(tc, labels); MVM_exception_throw_adhoc(tc, "Bytecode validation error: inconsistent operand types %d and %d to op '%s' with a type variable, at instruction %d", static_frame->local_types[GET_REG(cur_op, 0)], operand_type_var >> 3, op_info->name, instruction); } } else { operand_type_var = (static_frame->local_types[GET_REG(cur_op, 0)] << 3); } } else if ((static_frame->local_types[GET_REG(cur_op, 0)] << 3) != op_type) {
static void bytecode_dump_frame_internal(MVMThreadContext *tc, MVMStaticFrame *frame, MVMSpeshCandidate *maybe_candidate, MVMuint8 *frame_cur_op, char ***frame_lexicals, char **oo, MVMuint32 *os, MVMuint32 *ol) { /* since "references" are not a thing in C, keep a local copy of these * and update the passed-in pointers at the end of the function */ char *o = *oo; MVMuint32 s = *os; MVMuint32 l = *ol; MVMuint32 i, j, k; /* mostly stolen from validation.c */ MVMStaticFrame *static_frame = frame; MVMuint32 bytecode_size = maybe_candidate ? maybe_candidate->bytecode_size : static_frame->body.bytecode_size; MVMuint8 *bytecode_start = maybe_candidate ? maybe_candidate->bytecode : static_frame->body.bytecode; MVMuint8 *bytecode_end = bytecode_start + bytecode_size; /* current position in the bytestream */ MVMuint8 *cur_op = bytecode_start; /* positions in the bytestream that are starts of ops and goto targets */ MVMuint8 *labels = MVM_calloc(1, bytecode_size); MVMuint32 *jumps = MVM_calloc(1, sizeof(MVMuint32) * bytecode_size); char **lines = MVM_malloc(sizeof(char *) * bytecode_size); MVMuint32 *linelocs = MVM_malloc(sizeof(MVMuint32) * bytecode_size); MVMuint32 lineno = 0; MVMuint32 lineloc; MVMuint16 op_num; const MVMOpInfo *op_info; MVMuint32 operand_size = 0; unsigned char op_rw; unsigned char op_type; unsigned char op_flags; MVMOpInfo tmp_extop_info; /* stash the outer output buffer */ MVMuint32 sP = s; MVMuint32 lP = l; char *oP = o; char *tmpstr; char mark_this_line = 0; MVMCompUnit *cu = static_frame->body.cu; while (cur_op < bytecode_end - 1) { /* allocate a line buffer */ s = 200; l = 0; o = MVM_calloc(s, sizeof(char)); lineloc = cur_op - bytecode_start; /* mark that this line starts at this point in the bytestream */ linelocs[lineno] = lineloc; /* mark that this point in the bytestream is an op boundary */ labels[lineloc] |= MVM_val_op_boundary; mark_this_line = 0; if (frame_cur_op) { if (frame_cur_op == cur_op || frame_cur_op == cur_op + 2) { mark_this_line = 1; } } if (mark_this_line) { a("-> "); } else { a(" "); } op_num = *((MVMint16 *)cur_op); cur_op += 2; if (op_num < MVM_OP_EXT_BASE) { op_info = MVM_op_get_op(op_num); if (op_info) a("%-18s ", op_info->name); else a("invalid OP "); } else { MVMint16 ext_op_num = op_num - MVM_OP_EXT_BASE; if (0 <= ext_op_num && ext_op_num < cu->body.num_extops) { MVMExtOpRecord r = cu->body.extops[ext_op_num]; MVMuint8 j; memset(&tmp_extop_info, 0, sizeof(MVMOpInfo)); tmp_extop_info.name = MVM_string_utf8_encode_C_string(tc, r.name); memcpy(tmp_extop_info.operands, r.operand_descriptor, 8); for (j = 0; j < 8; j++) if (tmp_extop_info.operands[j]) tmp_extop_info.num_operands++; else break; op_info = &tmp_extop_info; a("%-12s ", tmp_extop_info.name); MVM_free((void *)tmp_extop_info.name); tmp_extop_info.name = NULL; } else { a("Extension op %d out of range", (int)op_num); } } if (!op_info) continue; for (i = 0; i < op_info->num_operands; i++) { if (i) a(", "); op_flags = op_info->operands[i]; op_rw = op_flags & MVM_operand_rw_mask; op_type = op_flags & MVM_operand_type_mask; if (op_rw == MVM_operand_literal) { switch (op_type) { case MVM_operand_int8: operand_size = 1; a("%"PRId8, GET_I8(cur_op, 0)); break; case MVM_operand_int16: operand_size = 2; a("%"PRId16, GET_I16(cur_op, 0)); break; case MVM_operand_int32: operand_size = 4; a("%"PRId32, GET_I32(cur_op, 0)); break; case MVM_operand_int64: operand_size = 8; a("%"PRId64, MVM_BC_get_I64(cur_op, 0)); break; case MVM_operand_uint8: operand_size = 1; a("%"PRIu8, GET_I8(cur_op, 0)); break; case MVM_operand_uint16: operand_size = 2; a("%"PRIu16, GET_I16(cur_op, 0)); break; case MVM_operand_uint32: operand_size = 4; a("%"PRIu32, GET_I32(cur_op, 0)); break; case MVM_operand_uint64: operand_size = 8; a("%"PRIu64, MVM_BC_get_I64(cur_op, 0)); break; case MVM_operand_num32: operand_size = 4; a("%f", GET_N32(cur_op, 0)); break; case MVM_operand_num64: operand_size = 8; a("%f", MVM_BC_get_N64(cur_op, 0)); break; case MVM_operand_callsite: operand_size = 2; a("Callsite_%"PRIu16, GET_UI16(cur_op, 0)); break; case MVM_operand_coderef: operand_size = 2; a("Frame_%"PRIu16, GET_UI16(cur_op, 0)); break; case MVM_operand_str: operand_size = 4; if (GET_UI32(cur_op, 0) < cu->body.num_strings) { tmpstr = MVM_string_utf8_encode_C_string( tc, MVM_cu_string(tc, cu, GET_UI32(cur_op, 0))); /* XXX C-string-literal escape the \ and ' and line breaks and non-ascii someday */ a("'%s'", tmpstr); MVM_free(tmpstr); } else a("invalid string index: %d", GET_UI32(cur_op, 0)); break; case MVM_operand_ins: operand_size = 4; /* luckily all the ins operands are at the end of op operands, so I can wait to resolve the label to the end. */ if (GET_UI32(cur_op, 0) < bytecode_size) { labels[GET_UI32(cur_op, 0)] |= MVM_val_branch_target; jumps[lineno] = GET_UI32(cur_op, 0); } break; case MVM_operand_obj: /* not sure what a literal object is */ operand_size = 4; break; case MVM_operand_spesh_slot: operand_size = 2; a("sslot(%d)", GET_UI16(cur_op, 0)); break; default: fprintf(stderr, "what is an operand of type %d??\n", op_type); abort(); /* never reached, silence compiler warnings */ } } else if (op_rw == MVM_operand_read_reg || op_rw == MVM_operand_write_reg) { /* register operand */ MVMuint8 frame_has_inlines = maybe_candidate && maybe_candidate->num_inlines ? 1 : 0; MVMuint16 *local_types = frame_has_inlines ? maybe_candidate->local_types : frame->body.local_types; MVMuint16 num_locals = frame_has_inlines ? maybe_candidate->num_locals : frame->body.num_locals; operand_size = 2; a("loc_%u_%s", GET_REG(cur_op, 0), get_typename(local_types[GET_REG(cur_op, 0)])); } else if (op_rw == MVM_operand_read_lex || op_rw == MVM_operand_write_lex) { /* lexical operand */ MVMuint16 idx, frames, m; MVMStaticFrame *applicable_frame = static_frame; operand_size = 4; idx = GET_UI16(cur_op, 0); frames = GET_UI16(cur_op, 2); m = frames; while (m > 0) { applicable_frame = applicable_frame->body.outer; m--; } /* inefficient, I know. should use a hash. */ for (m = 0; m < cu->body.num_frames; m++) { if (get_frame(tc, cu, m) == applicable_frame) { char *lexname = frame_lexicals ? frame_lexicals[m][idx] : "lex??"; a("lex_Frame_%u_%s_%s", m, lexname, get_typename(applicable_frame->body.lexical_types[idx])); } } } cur_op += operand_size; } lines[lineno++] = o; } { MVMuint32 *linelabels = MVM_calloc(lineno, sizeof(MVMuint32)); MVMuint32 byte_offset = 0; MVMuint32 line_number = 0; MVMuint32 label_number = 1; MVMuint32 *annotations = MVM_calloc(lineno, sizeof(MVMuint32)); for (; byte_offset < bytecode_size; byte_offset++) { if (labels[byte_offset] & MVM_val_branch_target) { /* found a byte_offset where a label should be. now crawl up through the lines to find which line starts there */ while (line_number < lineno && linelocs[line_number] != byte_offset) line_number++; if (line_number < lineno) linelabels[line_number] = label_number++; } } o = oP; l = lP; s = sP; i = 0; /* resolve annotation line numbers */ for (j = 0; j < frame->body.num_annotations; j++) { MVMuint32 ann_offset = GET_UI32(frame->body.annotations_data, j*12); for (; i < lineno; i++) { if (linelocs[i] == ann_offset) { annotations[i] = j + 1; break; } } } for (j = 0; j < lineno; j++) { if (annotations[j]) { MVMuint16 shi = GET_UI16(frame->body.annotations_data + 4, (annotations[j] - 1)*12); tmpstr = MVM_string_utf8_encode_C_string( tc, MVM_cu_string(tc, cu, shi < cu->body.num_strings ? shi : 0)); a(" annotation: %s:%u\n", tmpstr, GET_UI32(frame->body.annotations_data, (annotations[j] - 1)*12 + 8)); MVM_free(tmpstr); } if (linelabels[j]) a(" label_%u:\n", linelabels[j]); a("%05d %s", j, lines[j]); MVM_free(lines[j]); if (jumps[j]) { /* horribly inefficient for large frames. again, should use a hash */ line_number = 0; while (line_number < lineno && linelocs[line_number] != jumps[j]) line_number++; if (line_number < lineno) a("label_%u(%05u)", linelabels[line_number], line_number); else a("label (invalid: %05u)", jumps[j]); } a("\n"); } MVM_free(lines); MVM_free(jumps); MVM_free(linelocs); MVM_free(linelabels); MVM_free(labels); MVM_free(annotations); } *oo = o; *os = s; *ol = l; }