/* * Find/create a class file node in the VM's class file table. * * If the class already exists, the hash must be the same, otherwise * a LinkageError is stored. Bump the reference count on the existing node. * * If no class by this name exists, add a new node with reference count 1. * * 'cbytes' should be NULL unless object generation is enabled and the * classfile was loaded by a user-defined class loader. If successful, * it will be copied to any newly created node. * * Stores an exception if unsuccessful. * * NOTE: This assumes the VM global mutex is locked. */ _jc_class_node * _jc_ref_class_node(_jc_env *env, const char *name, jlong hash, _jc_classbytes *cbytes) { _jc_jvm *const vm = env->vm; _jc_class_node *node; _jc_class_node key; size_t nlen; /* Sanity check */ _JC_MUTEX_ASSERT(env, vm->mutex); _JC_ASSERT(cbytes == NULL || cbytes->hash == hash); /* If code generation is disabled, don't save the class file bytes */ if (!vm->generation_enabled) cbytes = NULL; /* Search for existing node */ key.name = name; if ((node = _jc_splay_find(&vm->classfiles, &key)) != NULL) { /* Hash values must be the same */ if (hash != node->hash) { _JC_EX_STORE(env, LinkageError, "class file for `%s'" " has an unexpected hash value 0x%" _JC_JLONG_FMT " != 0x%" _JC_JLONG_FMT, name, hash, node->hash); return NULL; } /* Save class file if we don't have it yet */ if (cbytes != NULL && node->bytes == NULL) node->bytes = _jc_dup_classbytes(cbytes); /* Increment node reference count */ node->refs++; /* Return node */ return node; } /* Create a new class file node */ nlen = strlen(name); if ((node = _jc_vm_alloc(env, sizeof(*node) + nlen + 1)) == NULL) return NULL; memcpy(node + 1, name, nlen + 1); node->name = (char *)(node + 1); node->hash = hash; node->refs = 1; node->bytes = cbytes != NULL ? _jc_dup_classbytes(cbytes) : NULL; /* Add node to our classfile tree */ #ifndef NDEBUG memset(&node->node, 0, sizeof(node->node)); #endif _jc_splay_insert(&vm->classfiles, node); /* Done */ return node; }
void * _jc_vm_realloc(_jc_env *env, void *mem, size_t size) { void *new_mem; if ((new_mem = realloc(mem, size)) == NULL) { _JC_EX_STORE(env, OutOfMemoryError, "system heap"); return NULL; } return new_mem; }
void * _jc_vm_alloc(_jc_env *env, size_t size) { void *mem; if ((mem = malloc(size)) == NULL) { _JC_EX_STORE(env, OutOfMemoryError, "system heap"); return NULL; } return mem; }
char * _jc_vm_strdup(_jc_env *env, const char *s) { char *result; if ((result = strdup(s)) == NULL) { _JC_EX_STORE(env, OutOfMemoryError, "system heap"); return NULL; } return result; }
char * _jc_vm_strndup(_jc_env *env, const char *s, size_t len) { char *result; if ((result = malloc(len + 1)) == NULL) { _JC_EX_STORE(env, OutOfMemoryError, "system heap"); return NULL; } memcpy(result, s, len); result[len] = '\0'; return result; }
/* * Initialize a condition variable. * * If unsuccessful an exception is stored. */ jint _jc_cond_init(_jc_env *env, pthread_cond_t *cond) { int error; /* Initialize mutex attributes */ if ((error = pthread_cond_init(cond, NULL)) != 0) { _JC_EX_STORE(env, InternalError, "%s: %s", "pthread_cond_init", strerror(error)); return JNI_ERR; } /* Done */ return JNI_OK; }
/* * Initialize a mutex * * If unsuccessful an exception is stored. */ jint _jc_mutex_init(_jc_env *env, pthread_mutex_t *mutex) { pthread_mutexattr_t attr; int error; /* Initialize mutex attributes */ if ((error = pthread_mutexattr_init(&attr)) != 0) { _JC_EX_STORE(env, InternalError, "%s: %s", "pthread_mutexattr_init", strerror(error)); return JNI_ERR; } #if NDEBUG /* Enable debug checks */ if ((error = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) { _JC_EX_STORE(env, InternalError, "%s: %s", "pthread_mutexattr_settype", strerror(error)); pthread_mutexattr_destroy(&attr); return JNI_ERR; } #endif /* Initialize mutex */ if ((error = pthread_mutex_init(mutex, &attr)) != 0) { _JC_EX_STORE(env, InternalError, "%s: %s", "pthread_mutex_init", strerror(error)); pthread_mutexattr_destroy(&attr); return JNI_ERR; } /* Clean up */ pthread_mutexattr_destroy(&attr); return JNI_OK; }
/* * Resolve a symbol in an ELF object. There are two cases here. * * If 'resolver' is NULL, only symbols resolvable internally are * resolved, and exceptions are stored, not posted. Otherwise, * only symbols resolvable externally are resolved, 'resolver' is * used to resolve them, and exceptions are posted. */ static inline jint _jc_elf_resolve_sym(_jc_env *env, _jc_elf *elf, _jc_elf_loadable *loadable, const Elf_Rela *rela, _jc_elf_resolver *resolver, void *arg) { const Elf_Word type = ELF_R_TYPE(rela->r_info); _jc_elf_info *const info = elf->info; const Elf_Sym *const sym = &info->symbols[ELF_R_SYM(rela->r_info)]; const char *name = info->strings + sym->st_name; switch (sym->st_shndx) { case SHN_ABS: if (resolver != NULL) break; if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, sym->st_value, rela->r_addend) != JNI_OK) return JNI_ERR; break; case SHN_UNDEF: { Elf_Addr value; if (resolver == NULL) break; if ((*resolver)(env, arg, info->strings + sym->st_name, &value) != JNI_OK) return JNI_ERR; if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, value, rela->r_addend) != JNI_OK) { _jc_post_exception_info(env); return JNI_ERR; } break; } case SHN_COMMON: _JC_ASSERT(resolver == NULL); _JC_EX_STORE(env, LinkageError, "%s: ELF symbol `%s' is common (not supported)", elf->pathname, name); return JNI_ERR; default: { const _jc_elf_loadable *sym_section; /* Skip if only resolving externals */ if (resolver != NULL) break; /* Sanity check the symbol's section */ if (sym->st_shndx >= info->ehdr->e_shnum || (sym_section = info->shdr2section[sym->st_shndx]) == NULL) { _JC_EX_STORE(env, LinkageError, "%s: invalid section index %d for symbol `%s'", elf->pathname, sym->st_shndx, name); return JNI_ERR; } /* Apply relocation */ if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, (Elf_Addr)(sym_section->vaddr + sym->st_value), rela->r_addend) != JNI_OK) return JNI_ERR; break; } } /* Done */ return JNI_OK; }
/* * Process stabs line number information. * * If unsuccessful an exception is stored. */ jint _jc_debug_line_stabs(_jc_env *env, _jc_elf *elf, _jc_splay_tree *tree) { _jc_elf_info *const info = elf->info; const Elf_Shdr *const shdr = info->debug_lines.loadable.shdr; _jc_stab *const stabs = (_jc_stab *)(info->map_base + shdr->sh_offset); const int num_stabs = shdr->sh_size / sizeof(*stabs); _jc_map_state state; _jc_method *method; int i; /* Sanity check */ _JC_ASSERT(info->debug_lines.type == _JC_LINE_DEBUG_STABS); /* Initialize state */ memset(&state, 0, sizeof(state)); method = NULL; /* Run through stabs section */ for (i = 0; i < num_stabs; i++) { _jc_stab *const stab = &stabs[i]; /* Check type */ switch (stab->type) { case STAB_FUN: { _jc_method_node *node; _jc_method_node key; const char *fun; const char *s; /* Check for end of method; reset state if so */ if (stab->sindex == 0) { /* Were we skipping this method? */ if (method == NULL) continue; /* Finalize map */ if (_jc_debug_line_finish(env, method, elf->loader, &state) != JNI_OK) goto fail; method = NULL; break; } /* Already doing this function? */ if (method != NULL) continue; /* Get FUN string containing function name */ fun = (const char *)info->debug_lines.strings + stab->sindex; /* Sanity check not already within a method */ if (method != NULL) { _JC_EX_STORE(env, LinkageError, "double opening stabs FUN entry `%s'", fun); goto fail; } /* Try to parse out class & method name from symbol */ if (strncmp(fun, "_jc_", 4) != 0 || (s = strchr(fun + 4, '$')) == NULL || strncmp(s + 1, "method$", 7) != 0) continue; key.cname = fun + 4; key.clen = s - key.cname; key.mname = s + 8; if ((s = strchr(key.mname, ':')) == NULL) s += strlen(s); /* can this happen? */ key.mlen = s - key.mname; /* Find corresponding method node, if any */ if ((node = _jc_splay_find(tree, &key)) == NULL) continue; method = node->method; _JC_ASSERT(method != NULL); _JC_ASSERT(method->function != NULL); _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); _JC_ASSERT(method->u.exec.function_end != NULL); /* If method has no line number table, nothing to do */ if (method->u.exec.u.linenum.len == 0) { memset(&method->u.exec.u, 0, sizeof(method->u.exec.u)); method = NULL; continue; } /* Initialize state for this method */ _JC_ASSERT(state.pc_map.len == 0); _JC_ASSERT(state.last_linenum == 0); _JC_ASSERT(state.last_map == 0); state.linenum = method->u.exec.u.linenum; memset(&method->u.exec.u, 0, sizeof(method->u.exec.u)); break; } case STAB_SLINE: /* If skipping this method, skip lines */ if (method == NULL) continue; /* Add entry */ if (_jc_debug_line_add(env, &state, (const char *)method->function + stab->value, stab->desc) != JNI_OK) goto fail; break; default: break; } } /* Clean up and exit */ _jc_vm_free(&state.pc_map.map); return JNI_OK; fail: /* Clean up after failure */ _jc_vm_free(&state.pc_map.map); return JNI_ERR; }
/* * Process DWARF2 line number information. * * If unsuccessful an exception is stored. */ jint _jc_debug_line_dwarf2(_jc_env *env, _jc_elf *elf, _jc_splay_tree *tree) { _jc_elf_info *const info = elf->info; const Elf_Shdr *const shdr = info->debug_lines.loadable.shdr; const u_char *ptr = (const u_char *)info->map_base + shdr->sh_offset; const u_char *const section_end = ptr + shdr->sh_size; jboolean using64bit = JNI_FALSE; _jc_dwarf2_line_hdr *hdr; _jc_method_node **nodes; _jc_map_state state; _jc_method *method; unsigned long totlen; union _jc_value jvalue; int node_index; const u_char *ptr_end; const u_char *pc; int num_nodes; int cline; int i; /* Put nodes in a list and elide methods with no line number table */ if ((nodes = _JC_STACK_ALLOC(env, tree->size * sizeof(*nodes))) == NULL) goto fail; _jc_splay_list(tree, (void **)nodes); for (i = num_nodes = 0; i < tree->size; i++) { _jc_method_node *const node = nodes[i]; _jc_method *const method = node->method; _JC_ASSERT(method != NULL); _JC_ASSERT(method->function != NULL); _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); _JC_ASSERT(method->u.exec.function_end != NULL); if (method->u.exec.u.linenum.len > 0) nodes[num_nodes++] = nodes[i]; } /* Anything to do? */ if (num_nodes == 0) return JNI_OK; /* Sort methods by starting address */ qsort(nodes, num_nodes, sizeof(*nodes), _jc_method_addr_cmp); /* Initialize map state */ memset(&state, 0, sizeof(state)); node_index = 0; method = NULL; again: /* Read prologue header */ memcpy(&jvalue, ptr, sizeof(jint)); ptr += sizeof(jint); totlen = jvalue.i; if (totlen == 0xffffffff) { memcpy(&jvalue, ptr, sizeof(jlong)); ptr += sizeof(jlong); totlen = jvalue.j; using64bit = JNI_TRUE; } else if (totlen == 0) { memcpy(&jvalue, ptr, sizeof(jint)); ptr += sizeof(jint); totlen = jvalue.i; using64bit = JNI_TRUE; } ptr_end = ptr + totlen; ptr += 2; /* skip version */ ptr += 4; /* skip header len */ if (using64bit) ptr += 4; hdr = (_jc_dwarf2_line_hdr *)ptr; ptr += sizeof(*hdr) + hdr->opcode_base - 1; /* Skip over directory table */ while (*ptr++ != '\0') ptr += strlen((const char *)ptr) + 1; /* Skip over file name table */ while (*ptr++ != '\0') { ptr += strlen((const char *)ptr) + 1; (void)_jc_read_leb128(&ptr, JNI_FALSE); (void)_jc_read_leb128(&ptr, JNI_FALSE); (void)_jc_read_leb128(&ptr, JNI_FALSE); } /* Initialize statement program state */ pc = NULL; cline = 1; /* Process statement program */ while (ptr < ptr_end) { jboolean writeout = JNI_FALSE; jboolean reset = JNI_FALSE; u_char opcode; if ((opcode = *ptr++) >= hdr->opcode_base) { /* special */ opcode -= hdr->opcode_base; pc += (opcode / hdr->line_range) * hdr->minimum_instruction_length; cline += hdr->line_base + opcode % hdr->line_range; writeout = JNI_TRUE; } else if (opcode == 0) { /* extended */ unsigned int oplen; u_char exop; oplen = _jc_read_leb128(&ptr, JNI_FALSE); exop = *ptr++; switch (exop) { case DW_LNE_end_sequence: reset = JNI_TRUE; writeout = JNI_TRUE; break; case DW_LNE_set_address: { const u_char *new_pc; /* Get new PC */ memcpy(&new_pc, ptr, sizeof(new_pc)); /* We don't support out-of-spec reversals */ if (new_pc < pc) { _JC_EX_STORE(env, LinkageError, "address reversals in .debug_line" " section are not supported"); goto fail; } /* OK */ pc = new_pc; break; } default: break; } ptr += oplen - 1; } else { /* standard */ switch (opcode) { case DW_LNS_copy: writeout = JNI_TRUE; break; case DW_LNS_advance_pc: pc += _jc_read_leb128(&ptr, JNI_FALSE) * hdr->minimum_instruction_length; break; case DW_LNS_advance_line: cline += _jc_read_leb128(&ptr, JNI_TRUE); break; case DW_LNS_const_add_pc: pc += ((255 - hdr->opcode_base) / hdr->line_range) * hdr->minimum_instruction_length; break; case DW_LNS_fixed_advance_pc: { uint16_t advance; memcpy(&advance, ptr, 2); pc += advance; ptr += 2; break; } default: for (i = 0; i < hdr->standard_opcode_lengths[ opcode - 1]; i++) _jc_read_leb128(&ptr, JNI_FALSE); break; } } /* Have we reached the next method? */ if (method == NULL && (const void *)pc >= nodes[node_index]->method->function) { /* Initialize state for this method */ _JC_ASSERT(state.pc_map.len == 0); _JC_ASSERT(state.last_linenum == 0); _JC_ASSERT(state.last_map == 0); method = nodes[node_index]->method; _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); state.linenum = method->u.exec.u.linenum; memset(&method->u.exec.u, 0, sizeof(method->u.exec.u)); } /* Finished with the current method? */ if (method != NULL && (const void *)pc >= method->u.exec.function_end) { /* Finalize map for current method */ if (_jc_debug_line_finish(env, method, elf->loader, &state) != JNI_OK) goto fail; method = NULL; /* Look for next method */ if (++node_index == num_nodes) goto done; } /* Write matrix row */ if (writeout && method != NULL) { if (_jc_debug_line_add(env, &state, pc, cline) != JNI_OK) goto fail; } /* Reset after DW_LNE_end_sequence */ if (reset) { pc = NULL; cline = 1; } } if (ptr < section_end) goto again; done: /* Sanity check */ _JC_ASSERT(method == NULL); #ifndef NDEBUG for (i = 0; i < num_nodes; i++) { _jc_method_node *const node = nodes[i]; _jc_method *const method = node->method; _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); _JC_ASSERT(method->u.exec.u.pc_map.map != NULL); } #endif /* Done */ return JNI_OK; fail: /* Failed */ return JNI_ERR; }