You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by ge...@apache.org on 2005/10/05 04:20:10 UTC

svn commit: r294974 [13/25] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm: ./ jchevm/ jchevm/doc/ jchevm/etc/ jchevm/include/ jchevm/java/ jchevm/java/org/ jchevm/java/org/dellroad/ jchevm/java/org/dellroad/jc/ jchevm/java/org/dellroad...

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/elf.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/elf.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/elf.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/elf.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,884 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: elf.c,v 1.5 2005/02/23 22:48:16 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal functions */
+static jint		_jc_elf_load_link(_jc_env *env, _jc_elf *elf,
+				char **vaddrp, Elf_Off *vsizep);
+static void		_jc_elf_destroy_link_info(_jc_elf_info **infop);
+static jint		_jc_elf_resolve(_jc_env *env, _jc_elf *elf,
+				_jc_elf_resolver *resolver, void *arg);
+static 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);
+static jint		_jc_elf_process(_jc_env *env, _jc_elf *elf);
+
+/* Internal variables */
+static const u_char	elf_magic[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 };
+
+/*
+ * Load an ELF object file, resolve all symbols that can be resolved
+ * locally within the file, and do some initial processing. The ELF file
+ * is returned with one reference.
+ *
+ * If unsuccessful an exception is stored.
+ */
+_jc_elf *
+_jc_elf_load(_jc_env *env, _jc_class_loader *loader, const char *path)
+{
+	_jc_jvm *const vm = env->vm;
+	const size_t pathlen = strlen(path);
+	_jc_elf *elf;
+
+	/* Lock loader */
+	_JC_MUTEX_LOCK(env, loader->mutex);
+
+	/* Initialize new 'elf' structure */
+	if ((elf = _jc_cl_alloc(env, loader,
+	    sizeof(*elf) + pathlen + 1)) == NULL) {
+		_JC_MUTEX_UNLOCK(env, loader->mutex);
+		return NULL;
+	}
+
+	/* Unlock loader */
+	_JC_MUTEX_UNLOCK(env, loader->mutex);
+
+	/* Initialize ELF structure */
+	memset(elf, 0, sizeof(*elf));
+	elf->refs = 1;
+	elf->loader = loader;
+	memcpy(elf->pathname, path, pathlen + 1);
+	_jc_splay_init(&elf->types, _jc_type_cmp, _JC_OFFSETOF(_jc_type, node));
+
+	/* Verbosity */
+	if (loader == vm->boot.loader)
+		VERBOSE(OBJ, vm, "trying `%s' (via bootstrap loader)", path);
+	else {
+		VERBOSE(OBJ, vm, "trying `%s' (via %s@%p)",
+		    path, loader->instance->type->name, loader->instance);
+	}
+
+	/* Load object file */
+	if (_jc_elf_load_link(env, elf,
+	    &elf->vaddr, &elf->vsize) == JNI_ERR)
+		goto fail;
+
+	/* Verbosity */
+	if (loader == vm->boot.loader) {
+		VERBOSE(OBJ, vm, "processing `%s' (via bootstrap loader)",
+		    path);
+	} else {
+		VERBOSE(OBJ, vm, "processing `%s' (via %s@%p)",
+		    path, loader->instance->type->name, loader->instance);
+	}
+
+	/* Do initial linking */
+	if (_jc_elf_resolve(env, elf, NULL, NULL) != JNI_OK)
+		goto fail;
+
+	/* Do initial processing */
+	if (_jc_elf_process(env, elf) != JNI_OK)
+		goto fail;
+
+	/* Done */
+	return elf;
+
+fail:
+	VERBOSE(OBJ, vm, "failed: %s: %s",
+	    _jc_vmex_names[env->ex.num], env->ex.msg);
+	_jc_elf_unref(&elf);
+	return NULL;
+}
+
+/*
+ * Increment reference count on an ELF object.
+ */
+void
+_jc_elf_addref(_jc_elf *elf)
+{
+	_jc_word old_refs;
+
+	/* Increment ref count */
+	_JC_ASSERT(elf->refs > 0);
+	do
+		old_refs = elf->refs;
+	while (!_jc_compare_and_swap(&elf->refs, old_refs, old_refs + 1));
+}
+
+/*
+ * Unreference an ELF object.
+ */
+void
+_jc_elf_unref(_jc_elf **elfp)
+{
+	_jc_class_loader *loader;
+	_jc_elf *elf = *elfp;
+	_jc_word old_refs;
+
+	/* Sanity check */
+	if (elf == NULL)
+		return;
+	*elfp = NULL;
+
+	/* Decrement ref count */
+	_JC_ASSERT(elf->refs > 0);
+	do
+		old_refs = elf->refs;
+	while (!_jc_compare_and_swap(&elf->refs, old_refs, old_refs - 1));
+	if (elf->refs > 0)
+		return;
+
+	/* Destroy ELF link info */
+	_jc_elf_destroy_link_info(&elf->info);
+
+	/* Lock loader */
+	loader = elf->loader;
+	_JC_MUTEX_LOCK(_jc_get_current_env(), loader->mutex);
+
+	/* Try to give back class loader memory */
+	_jc_cl_unalloc(loader, &elf->vaddr, elf->vsize);
+	_jc_cl_unalloc(loader, &elf, sizeof(*elf) + strlen(elf->pathname) + 1);
+
+	/* Unlock loader */
+	_JC_MUTEX_UNLOCK(_jc_get_current_env(), loader->mutex);
+}
+
+/*
+ * Link a loaded ELF file and flush the instruction cache.
+ *
+ * It is assumed that internally resolvable symbols have already
+ * been resolved.
+ */
+jint
+_jc_elf_link(_jc_env *env, _jc_elf *elf, _jc_elf_resolver *resolver, void *arg)
+{
+	_jc_elf_info *const info = elf->info;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(resolver != NULL);
+	_JC_ASSERT(elf->info != NULL);
+
+	/* Resolve external references */
+	if (_jc_elf_resolve(env, elf, resolver, arg) != JNI_OK)
+		return JNI_ERR;
+
+	/* Flush the i-cache for executable sections */
+	for (i = 0; i < info->num_loadables; i++) {
+		_jc_elf_loadable *const loadable = &info->loadables[i];
+		const Elf_Shdr *const shdr = loadable->shdr;
+
+		if ((shdr->sh_flags & SHF_EXECINSTR) != 0)
+			_jc_iflush(loadable->vaddr, shdr->sh_size);
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Free up memory used by the linking and symbol resolution process.
+ */
+void
+_jc_elf_link_cleanup(_jc_elf *elf)
+{
+	_jc_elf_destroy_link_info(&elf->info);
+}
+
+/*
+ * Load and ELF file but don't do anything much with it.
+ *
+ * The object is loaded using class loader memory, in a region
+ * described upon return by *vaddrp and *vsizep.
+ *
+ * If unsuccessful an exception is stored.
+ */
+static jint
+_jc_elf_load_link(_jc_env *env, _jc_elf *elf, char **vaddrp, Elf_Off *vsizep)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_class_loader *const loader = elf->loader;
+	const Elf_Ehdr *ehdr;
+	const Elf_Shdr *symtab = NULL;
+	const Elf_Shdr *strtab = NULL;
+	const Elf_Shdr *stabstr = NULL;
+	const char *shdr_strings;
+	char *aligned_vaddr;
+	_jc_elf_info *info;
+	Elf_Off max_align;
+	char *vaddr = NULL;
+	Elf_Off vsize = 0;
+	Elf_Off offset;
+	struct stat sb;
+	int esave;
+	int fd;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(elf->info == NULL);
+
+	/* Initialize new ELF linkage structure */
+	if ((info = _jc_vm_zalloc(env, sizeof(*info))) == NULL)
+		return JNI_ERR;
+
+	/* Open the ELF file and mmap() it in read-only */
+	if ((fd = open(elf->pathname, O_RDONLY)) == -1 || fstat(fd, &sb) == -1)
+		goto fail_errno;
+	(void)fcntl(fd, F_SETFD, 1);
+	info->map_size = sb.st_size;
+	info->map_base = mmap(NULL, info->map_size,
+	    PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+	esave = errno;
+	close(fd);
+	if (info->map_base == MAP_FAILED) {
+		errno = esave;
+		goto fail_errno;
+	}
+	ehdr = info->ehdr = (const Elf_Ehdr *)info->map_base;
+
+	/* Check that we're looking at the right kind of file */
+	if (memcmp(ehdr, elf_magic, sizeof(elf_magic)) != 0) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' is not an ELF object file", elf->pathname);
+		goto fail_errmsg;
+	}
+	if (ehdr->e_ident[EI_CLASS] != _JC_ELF_CLASS
+	    || ehdr->e_ident[EI_DATA] != _JC_ELF_DATA
+	    || ehdr->e_machine != _JC_ELF_MACHINE
+	    || ehdr->e_shentsize != sizeof(Elf_Shdr)) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' has an unsupported ELF format", elf->pathname);
+		goto fail_errmsg;
+	}
+	if (ehdr->e_ident[EI_VERSION] != EV_CURRENT
+	    || ehdr->e_version != EV_CURRENT) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' has an unsupported ELF version", elf->pathname);
+		goto fail_errmsg;
+	}
+	if (ehdr->e_type != ET_REL) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' is not a relocatable object", elf->pathname);
+		goto fail_errmsg;
+	}
+	if (ehdr->e_shoff == 0 || ehdr->e_shstrndx == SHN_UNDEF) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' is missing required section header info",
+		    elf->pathname);
+		goto fail_errmsg;
+	}
+
+	/* Initialize pointers to section header table and its strings */
+	info->shdrs = (const Elf_Shdr *)(info->map_base + ehdr->e_shoff);
+	shdr_strings = info->map_base + info->shdrs[ehdr->e_shstrndx].sh_offset;
+
+	/* Create array of loadable sections */
+	if ((info->loadables = _jc_vm_zalloc(env,
+	    ehdr->e_shnum * sizeof(*info->loadables))) == NULL)
+		goto fail;
+
+	/* Create array that maps section index back to loadable section */
+	if ((info->shdr2section = _jc_vm_alloc(env,
+	    ehdr->e_shnum * sizeof(*info->shdr2section))) == NULL)
+		goto fail;
+
+	/* Scan the section header entries for loadable sections */
+	offset = 0;
+	max_align = _JC_FULL_ALIGNMENT;
+	for (info->num_loadables = i = 0; i < ehdr->e_shnum; i++) {
+		const Elf_Shdr *const shdr = &info->shdrs[i];
+		const char *const sname = shdr_strings + shdr->sh_name;
+		_jc_elf_loadable *const loadable
+		    = &info->loadables[info->num_loadables];
+
+		/* Ignore non-loadable sections from this point on */
+		if ((shdr->sh_flags & SHF_ALLOC) == 0)
+			continue;
+
+		/* Sanity check */
+		if (shdr->sh_addr != 0) {
+			snprintf(env->ex.msg, sizeof(env->ex.msg),
+			    "section `%s' in `%s' has non-zero section"
+			    " load address", sname, elf->pathname);
+			goto fail_errmsg;
+		}
+
+		/* Initialize new loadable section */
+		memset(loadable, 0, sizeof(*loadable));
+		loadable->name = sname;
+		loadable->shdr = shdr;
+		loadable->bytes = info->map_base + shdr->sh_offset;
+		info->shdr2section[i] = loadable;
+
+		/* Keep track of maximum alignment for all loadable sections */
+		if (shdr->sh_addralign > max_align)
+			max_align = shdr->sh_addralign;
+
+		/* Align the load offset for this section */
+		if (shdr->sh_addralign > _JC_FULL_ALIGNMENT)
+			offset = _JC_ROUNDUP2(offset, shdr->sh_addralign);
+		loadable->offset = offset;
+
+		/*
+		 * Update offset to account for this section's length.
+		 * Keep all sections aligned at least to _JC_FULL_ALIGNMENT.
+		 */
+		offset = _JC_ROUNDUP2(offset + shdr->sh_size,
+		    _JC_FULL_ALIGNMENT);
+
+		/* Continue */
+		info->num_loadables++;
+	}
+
+	/* Scan section header entries for debug, symbol and string sections */
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		const Elf_Shdr *const shdr = &info->shdrs[i];
+		const char *const sname = shdr_strings + shdr->sh_name;
+		_jc_elf_debug_lines *const dlines = &info->debug_lines;
+
+		/* Remember symbol and string sections */
+		if (strcmp(sname, ".symtab") == 0) {
+			symtab = shdr;
+			continue;
+		}
+		if (strcmp(sname, ".strtab") == 0) {
+			strtab = shdr;
+			continue;
+		}
+		if (strcmp(sname, ".stabstr") == 0) {
+			stabstr = shdr;			/* stabs strings */
+			continue;
+		}
+
+		/* Remember debug sections containing line number info */
+		if (!vm->line_numbers)
+			continue;
+		if (strcmp(sname, ".debug_line") == 0)
+			dlines->type = _JC_LINE_DEBUG_DWARF2;
+		else if (strcmp(sname, ".stab") == 0)
+			dlines->type = _JC_LINE_DEBUG_STABS;
+		else
+			continue;
+		dlines->loadable.name = sname;
+		dlines->loadable.shdr = shdr;
+		dlines->loadable.bytes = info->map_base + shdr->sh_offset;
+		dlines->loadable.vaddr = info->map_base + shdr->sh_offset;
+		info->shdr2section[i] = &dlines->loadable;
+	}
+
+	/* Get pointers to symbols and strings */
+	if (symtab == NULL || strtab == NULL) {
+		snprintf(env->ex.msg, sizeof(env->ex.msg),
+		    "`%s' is missing a symbol or string table",
+		    elf->pathname);
+		goto fail_errmsg;
+	}
+	info->symbols = (const Elf_Sym *)(info->map_base + symtab->sh_offset);
+	info->num_symbols = symtab->sh_size / sizeof(*info->symbols);
+	info->strings = info->map_base + strtab->sh_offset;
+
+	/* If debug section was found get other associated info */
+	switch (info->debug_lines.type) {
+	case _JC_LINE_DEBUG_DWARF2:
+	    {
+		_jc_elf_debug_lines *const dlines = &info->debug_lines;
+
+		for (i = 0; i < ehdr->e_shnum; i++) {
+			const Elf_Shdr *const shdr = &info->shdrs[i];
+			const char *const sname = shdr_strings + shdr->sh_name;
+
+			if (strcmp(sname, ".rel.debug_line") == 0)
+				dlines->loadable.rel = shdr;
+			else if (strcmp(sname, ".rela.debug_line") == 0)
+				dlines->loadable.rela = shdr;
+		}
+		break;
+	    }
+	case _JC_LINE_DEBUG_STABS:
+	    {
+		_jc_elf_debug_lines *const dlines = &info->debug_lines;
+
+		if (stabstr == NULL) {
+			snprintf(env->ex.msg, sizeof(env->ex.msg),
+			    "`%s' has no .stabstr section", elf->pathname);
+			goto fail_errmsg;
+		}
+		dlines->strings = info->map_base + stabstr->sh_offset;
+		break;
+	    }
+	case _JC_LINE_DEBUG_NONE:
+		break;
+	default:
+		_JC_ASSERT(JNI_FALSE);
+	}
+
+	/* Find relocation sections for all loadable sections */
+	for (i = 0; i < info->num_loadables; i++) {
+		_jc_elf_loadable *const loadable = &info->loadables[i];
+		char relaname[64];
+		char relname[64];
+		int j;
+
+		snprintf(relname, sizeof(relname), ".rel%s", loadable->name);
+		snprintf(relaname, sizeof(relaname), ".rela%s", loadable->name);
+		for (j = 0; j < ehdr->e_shnum; j++) {
+			const Elf_Shdr *const shdr = &info->shdrs[j];
+			const char *sname = shdr_strings + shdr->sh_name;
+
+			if (strcmp(sname, relname) == 0)
+				loadable->rel = shdr;
+			else if (strcmp(sname, relaname) == 0)
+				loadable->rela = shdr;
+		}
+	}
+
+	/*
+	 * Allocate memory for the loadable sections. Ensure that we can
+	 * align ourselves to 'max_align' by allocating a little extra.
+	 */
+	offset += max_align - _JC_FULL_ALIGNMENT;
+	vsize = offset;
+	_JC_MUTEX_LOCK(env, loader->mutex);
+	vaddr = _jc_cl_alloc(env, loader, vsize);
+	_JC_MUTEX_UNLOCK(env, loader->mutex);
+	if (vaddr == NULL)
+		goto fail;
+	aligned_vaddr = (void *)_JC_ROUNDUP2((Elf_Off)vaddr, max_align);
+
+	/* Load the loadable sections */
+	for (i = 0; i < info->num_loadables; i++) {
+		_jc_elf_loadable *const loadable = &info->loadables[i];
+		const Elf_Shdr *const shdr = loadable->shdr;
+
+		loadable->vaddr = aligned_vaddr + loadable->offset;
+		if (shdr->sh_type != SHT_NOBITS)
+			memcpy(loadable->vaddr, loadable->bytes, shdr->sh_size);
+		else
+			memset(loadable->vaddr, 0, shdr->sh_size);
+	}
+
+	/* Done */
+	*vaddrp = vaddr;
+	*vsizep = vsize;
+	elf->info = info;
+	return JNI_OK;
+
+fail_errno:
+	snprintf(env->ex.msg, sizeof(env->ex.msg),
+	    "%s: %s", elf->pathname, strerror(errno));
+
+fail_errmsg:
+	env->ex.num = _JC_LinkageError;
+
+fail:
+	/* Clean up after failure */
+	_JC_MUTEX_LOCK(env, loader->mutex);
+	_jc_cl_unalloc(loader, &vaddr, vsize);
+	_jc_elf_destroy_link_info(&info);
+	_JC_MUTEX_UNLOCK(env, loader->mutex);
+	return JNI_ERR;
+}
+
+/*
+ * Destroy an ELF linking structure.
+ *
+ * NOTE: This assume the associated class loader mutex is locked.
+ */
+static void
+_jc_elf_destroy_link_info(_jc_elf_info **infop)
+{
+	_jc_elf_info *info = *infop;
+
+	/* Sanity check */
+	if (info == NULL)
+		return;
+	*infop = NULL;
+
+	/* Destroy structure */
+	_JC_ASSERT(info->resolver == NULL);
+	munmap((void *)info->map_base, info->map_size);
+	_jc_vm_free(&info->shdr2section);
+	_jc_vm_free(&info->loadables);
+	_jc_vm_free(&info);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Resolve symbols in an ELF file.
+ *
+ * If 'resolver' is NULL, only symbols resolvable internally are
+ * resolved. Otherwise, only symbols resolvable externally are
+ * resolved, and 'resolver' is used to resolve them.
+ *
+ * Exceptions are posted if resolver != NULL, otherwise stored.
+ */
+static jint
+_jc_elf_resolve(_jc_env *env, _jc_elf *elf,
+	_jc_elf_resolver *resolver, void *arg)
+{
+	_jc_elf_info *const info = elf->info;
+	_jc_elf_debug_lines *const dlines = &info->debug_lines;
+	int i;
+
+	/* Resolve normal relocations for loadable sections */
+	for (i = 0; i < info->num_loadables; i++) {
+		_jc_elf_loadable *const section = &info->loadables[i];
+		const Elf_Shdr *const rsect = section->rel;
+		const Elf_Rel *rels;
+		int num_rels;
+		int j;
+
+		/* If no relocations, skip this section */
+		if (rsect == NULL)
+			continue;
+
+		/* Get pointer to relocations and count */
+		rels = (const Elf_Rel *)(info->map_base + rsect->sh_offset);
+		num_rels = rsect->sh_size / sizeof(*rels);
+
+		/* Process normal relocations */
+		for (j = 0; j < num_rels; j++) {
+			const Elf_Rel *const rel = &rels[j];
+			Elf_Rela rela;
+
+			rela.r_offset = rel->r_offset;
+			rela.r_info = rel->r_info;
+			rela.r_addend = 0;
+			if (_jc_elf_resolve_sym(env, elf,
+			    section, &rela, resolver, arg) != JNI_OK)
+				return JNI_ERR;
+		}
+	}
+
+	/* Resolve addend relocations for loadable sections */
+	for (i = 0; i < info->num_loadables; i++) {
+		_jc_elf_loadable *const section = &info->loadables[i];
+		const Elf_Shdr *const rsect = section->rela;
+		const Elf_Rela *relas;
+		int num_relas;
+		int j;
+
+		/* If no relocations, skip this section */
+		if (rsect == NULL)
+			continue;
+
+		/* Get pointer to relocations and count */
+		relas = (const Elf_Rela *)(info->map_base + rsect->sh_offset);
+		num_relas = rsect->sh_size / sizeof(*relas);
+
+		/* Process addend relocations */
+		for (j = 0; j < num_relas; j++) {
+			if (_jc_elf_resolve_sym(env, elf,
+			    section, &relas[j], resolver, arg) != JNI_OK)
+				return JNI_ERR;
+		}
+	}
+
+	/* Resolve normal relocations for debug line section, if any */
+	if (resolver == NULL && dlines->loadable.rel != NULL) {
+		_jc_elf_loadable *const section = &dlines->loadable;
+		const Elf_Shdr *const rsect = section->rel;
+		const Elf_Rel *rels;
+		int num_rels;
+		int j;
+
+		/* Get pointer to relocations and count */
+		rels = (const Elf_Rel *)(info->map_base + rsect->sh_offset);
+		num_rels = rsect->sh_size / sizeof(*rels);
+
+		/* Process normal relocations */
+		for (j = 0; j < num_rels; j++) {
+			const Elf_Rel *const rel = &rels[j];
+			Elf_Rela rela;
+
+			rela.r_offset = rel->r_offset;
+			rela.r_info = rel->r_info;
+			rela.r_addend = 0;
+			if (_jc_elf_resolve_sym(env, elf,
+			    section, &rela, NULL, NULL) != JNI_OK)
+				return JNI_ERR;
+		}
+	}
+
+	/* Resolve addend relocations for debug line section, if any */
+	if (resolver == NULL && dlines->loadable.rela != NULL) {
+		_jc_elf_loadable *const section = &dlines->loadable;
+		const Elf_Shdr *const rsect = section->rela;
+		const Elf_Rela *relas;
+		int num_relas;
+		int j;
+
+		/* Get pointer to relocations and count */
+		relas = (const Elf_Rela *)(info->map_base + rsect->sh_offset);
+		num_relas = rsect->sh_size / sizeof(*relas);
+
+		/* Process addend relocations */
+		for (j = 0; j < num_relas; j++) {
+			if (_jc_elf_resolve_sym(env, elf,
+			    section, &relas[j], NULL, NULL) != JNI_OK)
+				return JNI_ERR;
+		}
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+
+/*
+ * Scan a newly loaded ELF file and do the following:
+ *
+ * - Find all _jc_type definitions therein by scanning the symbol table
+ *   and put them in the ELF file's unloaded types tree.
+ *
+ * - Compute the ending address of all method functions, which is
+ *   computed using the 'size' attribute of the corresponding ELF symbol
+ *
+ * - Create the PC -> Java line number table index mapping, which is
+ *   computed using line number information in the ELF debug section
+ *
+ * This assumes that the initial intra-file ELF linking has been done.
+ *
+ * If unsuccessful, an exception is stored.
+ */
+static jint
+_jc_elf_process(_jc_env *env, _jc_elf *elf)
+{
+	_jc_elf_info *const info = elf->info;
+	_jc_method_node *node;
+	_jc_method_node key;
+	_jc_splay_tree methods;
+	_jc_method *method;
+	_jc_uni_mem uni;
+	int i;
+
+	/* Initialize temporary method info tree */
+	_jc_splay_init(&methods, _jc_method_node_compare,
+	    _JC_OFFSETOF(_jc_method_node, node));
+
+	/* Create temporary uni-allocator to hold _jc_method_node's */
+	_jc_uni_alloc_init(&uni, 0, NULL);
+
+	/* Scan symbol table and gather information about types & methods */
+	for (i = 0; i < info->num_symbols; i++) {
+		const Elf_Sym *const sym = &info->symbols[i];
+		const char *sym_name = info->strings + sym->st_name;
+		const _jc_elf_loadable *sym_section = NULL;
+		const char *s;
+		void *addr;
+
+		/* Check symbol type */
+		switch (sym->st_shndx) {
+		case SHN_ABS:
+			break;
+		case SHN_UNDEF:
+		case SHN_COMMON:
+			continue;
+		default:
+			/* Sanity check the symbol's section */
+			if (sym->st_shndx >= info->ehdr->e_shnum
+			    || (sym_section
+			      = info->shdr2section[sym->st_shndx]) == NULL)
+				continue;
+			break;
+		}
+
+		/* Check if symbol is a JC symbol */
+		if (strncmp(sym_name, "_jc_", 4) != 0
+		    || (s = strchr(sym_name + 4, '$')) == NULL)
+			continue;
+
+		/* Get symbol's value */
+		switch (sym->st_shndx) {
+		case SHN_ABS:				/* not likely! */
+			addr = (void *)sym->st_value;
+			break;
+		default:
+			addr = sym_section->vaddr + sym->st_value;
+			break;
+		}
+
+		/* Check if symbol is for a _jc_type */
+		if (strcmp(s + 1, "type") == 0) {
+			_jc_type *const type = addr;
+
+			_jc_splay_insert(&elf->types, type);
+			type->loader = elf->loader;
+			type->u.nonarray.u.elf = elf;
+			continue;
+		}
+
+		/* Check if symbol is for a _jc_method or method function */
+		if (strncmp(s + 1, "method", 6) != 0)
+			continue;
+		key.cname = sym_name + 4;
+		key.clen = s - key.cname;
+		s += 7;					/* skip "$method" */
+		if (*s == '$') {			/* function symbol */
+			key.mname = s + 1;
+			method = NULL;
+		} else if (strncmp(s, "_info$", 6) == 0) {
+			key.mname = s + 6;		/* _jc_method symbol */
+			method = (_jc_method *)addr;
+		} else
+			continue;			/* shouldn't happen */
+		key.mlen = strlen(key.mname);
+
+		/* Find/create method info node in tree */
+		if ((node = _jc_splay_find(&methods, &key)) == NULL) {
+
+			/* Don't create nodes for non-concrete methods */
+			if (method != NULL && method->function == NULL)
+				continue;
+
+			/* Create new node */
+			if ((node = _jc_uni_zalloc(env,
+			    &uni, sizeof(*node))) == NULL)
+				goto fail;
+			node->cname = key.cname;
+			node->clen = key.clen;
+			node->mname = key.mname;
+			node->mlen = key.mlen;
+			_jc_splay_insert(&methods, node);
+		}
+
+		/* Update node with method function size or method pointer */
+		if (method == NULL) {
+			_JC_ASSERT(node->size == 0);
+			_JC_ASSERT(sym->st_size > 0);
+			node->size = sym->st_size;
+		} else {
+			_JC_ASSERT(node->method == NULL);
+			node->method = method;
+		}
+
+		/* Compute function ending address if we have all the info */
+		if (node->method != NULL && node->size > 0) {
+			_JC_ASSERT(node->method->function != NULL);
+			_JC_ASSERT(!_JC_ACC_TEST(node->method, INTERP));
+			node->method->u.exec.function_end
+			    = (const char *)node->method->function + node->size;
+		}
+	}
+
+	/* Process any debug section containing line number info */
+	switch (info->debug_lines.type) {
+	case _JC_LINE_DEBUG_DWARF2:
+		if (_jc_debug_line_dwarf2(env, elf, &methods) != JNI_OK)
+			goto fail;
+		break;
+	case _JC_LINE_DEBUG_STABS:
+		if (_jc_debug_line_stabs(env, elf, &methods) != JNI_OK)
+			goto fail;
+		break;
+	default:
+		break;
+	}
+
+	/* Done */
+	_jc_uni_alloc_free(&uni);
+	return JNI_OK;
+
+fail:
+	/* Clean up after failure */
+	_jc_uni_alloc_free(&uni);
+	return JNI_ERR;
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/exception.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/exception.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/exception.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/exception.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,539 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: exception.c,v 1.15 2005/03/16 15:31:12 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal functions */
+static void	_jc_bootstrap_exception(_jc_env *env, int num, const char *msg);
+
+/*
+ * Post one of the special, pre-instatiated VM exceptions.
+ *
+ * 'env' may be NULL.
+ */
+void
+_jc_post_exception(_jc_env *env, int num)
+{
+	_jc_post_exception_msg(env, num, NULL);
+}
+
+/*
+ * Post an exception using an exception info structure.
+ */
+void
+_jc_post_exception_info(_jc_env *env)
+{
+	_JC_ASSERT(env->ex.num >= 0 && env->ex.num < _JC_VMEXCEPTION_MAX);
+	if (*env->ex.msg != '\0')
+		_jc_post_exception_msg(env, env->ex.num, "%s", env->ex.msg);
+	else
+		_jc_post_exception_msg(env, env->ex.num, NULL);
+#ifndef NDEBUG
+	env->ex.num = -1;
+#endif
+}
+
+/*
+ * Post one of the special VM exceptions with the optional message string.
+ *
+ * 'env' may be NULL.
+ */
+void
+_jc_post_exception_msg(_jc_env *env, int num, const char *fmt, ...)
+{
+	_jc_jvm *const vm = env->vm;
+	jobject sref = NULL;
+	char *msgbuf = NULL;
+	_jc_word param;
+	va_list args;
+
+	/* Sanity check */
+	_JC_ASSERT(num >= 0 && num < _JC_VMEXCEPTION_MAX);
+
+	/* Format the message string */
+	if (fmt != NULL) {
+		va_start(args, fmt);
+		msgbuf = _JC_FORMAT_STRINGV(env, fmt, args);
+		va_end(args);
+	}
+
+	/* Handle an exception thrown during the bootstrap process */
+	if (vm == NULL || vm->initialization != NULL) {
+		if (vm == NULL || !vm->initialization->may_execute) {
+			_jc_bootstrap_exception(env, num, msgbuf);
+			return;
+		}
+		if (vm->boot.objects.vmex[num] == NULL) {
+			_JC_ASSERT(vm->initialization != NULL);
+			_jc_fatal_error(vm, "%s thrown during bootstrap",
+			    _jc_vmex_names[num]);
+			return;
+		}
+	}
+
+	/* Create message String object */
+	if (msgbuf != NULL) {
+		if ((sref = _jc_new_local_native_ref(env,
+		    _jc_new_string(env, msgbuf, strlen(msgbuf)))) == NULL)
+			return;
+		param = (_jc_word)*sref;
+	} else
+		param = (_jc_word)NULL;
+
+	/* Create and post the exception */
+	_jc_post_exception_params(env, num, &param);
+
+	/* Clean up */
+	_jc_free_local_native_ref(&sref);
+}
+
+/*
+ * Instantiate and post one of the special VM exceptions with the
+ * supplied constructor parameters. The parameters must match the
+ * constructor resolved for this exception during bootstrapping.
+ *
+ * This assumes env != NULL and that we are past the point during
+ * bootstrap where code execution is enabled.
+ *
+ * If we are already trying to post it, use one the pre-instantiated
+ * one to avoid infinite recursion.
+ */
+void
+_jc_post_exception_params(_jc_env *env, int num, _jc_word *params)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_method *const cons = vm->boot.methods.vmex[num].init;
+	_jc_object *e;
+
+	/* Sanity check */
+	_JC_ASSERT(env != NULL);
+	_JC_ASSERT(env->head.pending == NULL);
+	_JC_ASSERT(num >= 0 && num < _JC_VMEXCEPTION_MAX);
+	_JC_ASSERT(vm->initialization == NULL
+	    || vm->initialization->may_execute);
+
+	/* Don't recursively try throw the same exception */
+	if ((env->in_vmex & (1 << num)) != 0) {
+
+		/* Pre-instantiated exception may not exist during bootstrap */
+		if ((e = vm->boot.objects.vmex[num]) == NULL) {
+			_JC_ASSERT(vm->initialization != NULL);
+			_jc_fatal_error(vm, "%s recursively thrown"
+			    " during bootstrap", _jc_vmex_names[num]);
+			return;
+		}
+
+		/* Print verbose message */
+		if ((env->vm->verbose_flags
+		    & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+			_jc_printf(vm, "[verbose %s: %s recursively thrown]\n",
+			    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS],
+			    _jc_vmex_names[num]);
+		}
+
+		/* Just post the pre-instantiated version of this exception */
+		goto post_it;
+	}
+
+	/* Enable recursion detection */
+	env->in_vmex |= (1 << num);
+
+	/* Verbosity */
+	if ((env->vm->verbose_flags & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+		char *buf = NULL;
+		size_t len;
+
+		/* Decode first paramater if it's a message string */
+		if (strncmp(cons->signature, "(Ljava/lang/String;", 19) == 0
+		    && params[0] != (_jc_word)NULL) {
+			_jc_object *const string = (_jc_object *)params[0];
+
+			len = _jc_decode_string_utf8(env, string, NULL);
+			if ((buf = _jc_vm_alloc(env, len + 1)) != NULL)
+				_jc_decode_string_utf8(env, string, buf);
+		}
+
+		/* Print verbose message */
+		_jc_printf(vm, "[verbose %s: posting in thread %p: %s%s%s]\n",
+		    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS], env,
+		    _jc_vmex_names[num], buf != NULL ? ": " : "",
+		    buf != NULL ? buf : "");
+		_jc_vm_free(&buf);
+	}
+
+	/* Create new exception object */
+	if ((e = _jc_new_object(env, vm->boot.types.vmex[num])) == NULL)
+		goto done;
+
+	/* Invoke constructor with supplied parameters */
+	if (_jc_invoke_nonvirtual_a(env, cons, e, params) != JNI_OK)
+		goto done;
+
+post_it:
+	/* Post the exception to this thread */
+	env->head.pending = e;
+
+done:
+	/* Clean up */
+	env->in_vmex &= ~(1 << num);
+}
+
+/*
+ * Post an exception using an already-instatiated Throwable object.
+ *
+ * Any previously posted exception overrides 'exception'.
+ */
+void
+_jc_post_exception_object(_jc_env *env, _jc_object *exception)
+{
+	_jc_jvm *const vm = env->vm;
+
+	/* Sanity check */
+	_JC_ASSERT(_jc_subclass_of(exception, env->vm->boot.types.Throwable));
+
+	/* Verbosity */
+	if ((env->vm->verbose_flags & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+		_jc_printf(vm, "[verbose %s: posting in thread %p: ",
+		    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS], env);
+		_jc_fprint_exception_headline(env, stdout, exception);
+		_jc_printf(vm, "]\n");
+	}
+
+	/* Post the exception to this thread */
+	_JC_ASSERT(env->head.pending == NULL);
+	env->head.pending = exception;
+}
+
+/*
+ * Handle an exception during VM creation. Instead of trying to create
+ * an actual Throwable object, we just store the exception type, message,
+ * and stack trace in the vm->initialization structure.
+ */
+static void
+_jc_bootstrap_exception(_jc_env *env, int num, const char *msg)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_initialization *const init = vm->initialization;
+	int num_frames;
+
+	/* Sanity check */
+	_JC_ASSERT(vm == NULL || vm->initialization != NULL);
+
+	/* Discard any previous bootstrap exception */
+	init->ex.num = -1;
+	init->num_frames = 0;
+	_jc_vm_free(&init->frames);
+
+	/* "Post" new exception */
+	init->ex.num = num;
+
+	/* Verbosity */
+	VERBOSE(EXCEPTIONS, env->vm, "bootstrap exception: %s: %s",
+	    _jc_vmex_names[num], msg);
+
+	/* Avoid infinite recursion */
+	if ((env->in_vmex & (1 << _JC_OutOfMemoryError)) != 0)
+		return;
+
+	/* Save message */
+	if (msg != NULL) {
+		strncpy(init->ex.msg, msg, sizeof(init->ex.msg) - 1);
+		init->ex.msg[sizeof(init->ex.msg) - 1] = '\0';
+	} else
+		*init->ex.msg = '\0';
+
+	/* Save stack trace */
+	num_frames = _jc_save_stack_frames(env, env, 0, NULL);
+	if ((init->frames = _jc_vm_alloc(env,
+	    num_frames * sizeof(*init->frames))) == NULL)
+		return;
+	init->num_frames = num_frames;
+	_jc_save_stack_frames(env, env, init->num_frames, init->frames);
+}
+
+/*
+ * Throw an exception.
+ *
+ * It must have already been posted.
+ */
+void
+_jc_throw_exception(_jc_env *env)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_stack_crawl crawl;
+	_jc_catch_frame *catch;
+	int target_index = 1;
+
+	/* Sanity check */
+	_JC_ASSERT(env->status == _JC_THRDSTAT_RUNNING_NORMAL
+	    || env->status == _JC_THRDSTAT_HALTING_NORMAL);
+	_JC_ASSERT(vm->initialization == NULL
+	    || vm->initialization->may_execute);
+	_JC_ASSERT(env->head.pending != NULL);
+
+	/* Retrieve and un-post the exception */
+	env->head.caught = _jc_retrieve_exception(env, NULL);
+
+	/* Verbosity */
+	if ((env->vm->verbose_flags & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+		_jc_printf(vm, "[verbose %s: throwing in thread %p: ",
+		    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS], env);
+		_jc_fprint_exception_headline(env, stdout, env->head.caught);
+		_jc_printf(vm, "]\n");
+	}
+
+	/* Lock VM */
+	_JC_MUTEX_LOCK(env, vm->mutex);
+
+	/*
+	 * Crawl up the stack, searching for a method that:
+	 *
+	 *   (a) Matches the current catch frame (i.e., same method); and
+	 *   (b) Has a matching trap table entry for the exception
+	 *
+	 * If we find a method that satisfies (a) but not (b) then
+	 * the current catch frame is discarded, as it corresponds to
+	 * a frame that might have caught the exception but didn't.
+	 * The corresponding method must be (indirectly) recursive.
+	 */
+	catch = env->head.catch_list;
+	for (_jc_stack_crawl_first(env, &crawl);
+	    JNI_TRUE; _jc_stack_crawl_next(vm, &crawl)) {
+		_jc_method *const method = crawl.method;
+		int i;
+
+		/* We should never run out of stack or catch frames */
+		_JC_ASSERT(method != NULL);
+		_JC_ASSERT(catch != NULL);
+		_JC_ASSERT(!_JC_ACC_TEST(method, INTERP));
+
+		/* Does this frame's method correspond to the catch frame? */
+		if (method != catch->method)
+			continue;
+
+		/*
+		 * If the method doesn't have a trap table, it must be
+		 * _jc_invoke_jcni_a(), which catches all exceptions.
+		 */
+		if (method->u.exec.trap_table_len == 0) {
+			_JC_ASSERT(method == &vm->invoke_method);
+			if ((env->vm->verbose_flags
+			    & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+				_jc_printf(vm, "[verbose %s: caught via %s()"
+				    " in thread %p: ",
+				    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS],
+				    method->name, env);
+				_jc_fprint_exception_headline(env,
+				    stdout, env->head.caught);
+				_jc_printf(vm, "]\n");
+			}
+			break;
+		}
+		_JC_ASSERT(method->class != NULL);
+
+		/* If not within any trap region, skip the trap scan */
+		if (catch->region == 0)
+			goto not_caught;
+
+		/* Search trap table for a matching range and exception */
+		for (i = 0; i < method->u.exec.trap_table_len; i++) {
+			_jc_trap_info *const trap
+			    = &method->u.exec.trap_table[i];
+
+			/* Sanity check */
+			_JC_ASSERT(trap->start > 0 && trap->end > trap->start);
+
+			/* Did the exception happen within the trap region? */
+			if (catch->region < trap->start
+			    || catch->region >= trap->end)
+				continue;
+
+			/* Does the exception type match what's caught? */
+			if (trap->type != NULL
+			    && !_jc_subclass_of(env->head.caught, trap->type))
+				continue;
+
+			/* Derive trap target index from trap table index */
+			target_index = i + 1;
+
+			/* Verbosity */
+			if ((env->vm->verbose_flags
+			    & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+				_jc_printf(vm, "[verbose %s: caught via trap"
+				    " %d (%d-%d) in %s.%s%s in thread %p: ",
+				    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS],
+				    target_index - 1, trap->start, trap->end,
+				    method->class->name, method->name,
+				    method->signature, env);
+				_jc_fprint_exception_headline(env,
+				    stdout, env->head.caught);
+				_jc_printf(vm, "]\n");
+			}
+
+			/* Unwind the stack */
+			goto found;
+		}
+
+not_caught:
+		/* Exception not caught; discard the current catch frame */
+		catch = catch->next;
+	}
+
+found:
+	/* Unlock VM */
+	_JC_MUTEX_UNLOCK(env, vm->mutex);
+
+	/* Unlink all catch frames added after the target catch frame */
+	env->head.catch_list = catch;
+
+	/* Reset the top Java stack frame */
+	env->java_stack = crawl.stack;
+	_JC_ASSERT(env->java_stack == NULL || !env->java_stack->interp);
+	((_jc_exec_stack *)env->java_stack)->pc = NULL;
+#ifndef NDEBUG
+	_jc_stack_frame_init(&((_jc_exec_stack *)env->java_stack)->frame);
+#endif
+
+	/* Jump back to the catching method, supplying the target trap index */
+	_JC_ASSERT(target_index > 0);
+	siglongjmp(catch->context, target_index);
+}
+
+/*
+ * Retrieve this thread's pending exception and un-post it, if any.
+ *
+ * If "type" is not NULL, only retrieve the exception if it is an
+ * instance of the type (otherwise return NULL and leave it posted).
+ */
+_jc_object *
+_jc_retrieve_exception(_jc_env *env, _jc_type *type)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_object *const e = env->head.pending;
+
+	/* Sanity check */
+	_JC_ASSERT(env == _jc_get_current_env());
+	_JC_ASSERT(e != NULL);
+
+	/* If type doesn't match, return NULL and leave it posted */
+	if (type != NULL && !_jc_subclass_of(e, type))
+		return NULL;
+
+	/* Verbosity */
+	if ((env->vm->verbose_flags & (1 << _JC_VERBOSE_EXCEPTIONS)) != 0) {
+		_jc_printf(vm, "[verbose %s: unposting in thread %p: ",
+		    _jc_verbose_names[_JC_VERBOSE_EXCEPTIONS], env);
+		_jc_fprint_exception_headline(env, stdout, e);
+		_jc_printf(vm, "]\n");
+	}
+
+	/* Un-post the exception and return it */
+	env->head.pending = NULL;
+	return e;
+}
+
+/*
+ * Atomically retrieve the cross-posted exception, if any.
+ */
+_jc_object *
+_jc_retrieve_cross_exception(_jc_env *env)
+{
+	_jc_object *e;
+
+	/*
+	 * Check for an exception posted by another thread.
+	 * Retrieve it atomically so as to not lose any.
+	 */
+	while ((e = env->cross_exception) != NULL) {
+		if (_jc_compare_and_swap((_jc_word *)&env->cross_exception,
+		    (_jc_word)e, (_jc_word)NULL))
+			break;
+	}
+	return e;
+}
+
+/*
+ * Version of _jc_retrieve_exception() suitable for use by code
+ * in the bootstrap path, when the exception may not be a real object.
+ * The exeception is not retrieved, it's only unposted.
+ */
+jboolean
+_jc_unpost_exception(_jc_env *env, int num)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_initialization *const init = vm->initialization;
+
+	/* Are we no longer doing special bootstrap exceptions? */
+	if (vm->initialization == NULL || vm->initialization->may_execute) {
+		return _jc_retrieve_exception(env,
+		    vm->boot.types.vmex[num]) != NULL;
+	}
+
+	/* Does "posted" exception type match? */
+	if (init->ex.num != num)
+		return JNI_FALSE;
+
+	/* Verbosity */
+	VERBOSE(EXCEPTIONS, env->vm, "unposting bootstrap exception: %s%s%s",
+	    _jc_vmex_names[init->ex.num], *init->ex.msg != '\0' ? ": " : "",
+	    init->ex.msg);
+
+	/* Unpost exception */
+	_jc_vm_free(&init->frames);
+	init->ex.num = -1;
+	init->num_frames = 0;
+	return JNI_TRUE;
+}
+
+/*
+ * Extract headline from an exception object.
+ */
+void
+_jc_fprint_exception_headline(_jc_env *env, FILE *fp, _jc_object *e)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_object *message;
+	char *msg_buf;
+	size_t msg_len;
+
+	/* Sanity check */
+	_JC_ASSERT(e != NULL);
+	_JC_ASSERT(_jc_subclass_of(e, vm->boot.types.Throwable));
+
+	/* Print exception class */
+	_jc_fprint_noslash(vm, fp, e->type->name);
+
+	/* Any detail message? */
+	if ((message = *_JC_VMFIELD(vm, e,
+	    Throwable, detailMessage, _jc_object *)) == NULL)
+		return;
+
+	/* Print exception detail message, if any */
+	msg_len = _jc_decode_string_utf8(env, message, NULL);
+	if ((msg_buf = _JC_STACK_ALLOC(env, msg_len + 1)) == NULL) {
+		_jc_fprintf(vm, fp, ": [error displaying message: %s%s%s]",
+		    _jc_vmex_names[env->ex.num],
+		    env->ex.msg != '\0' ? ": " : "", env->ex.msg);
+	} else {
+		_jc_decode_string_utf8(env, message, msg_buf);
+		_jc_fprintf(vm, fp, ": %s", msg_buf);
+	}
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/fatal.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/fatal.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/fatal.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/fatal.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,58 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: fatal.c,v 1.1.1.1 2004/02/20 05:15:34 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+void
+_jc_fatal_error(_jc_jvm *vm, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	_jc_fatal_error_v(vm, fmt, args);
+	/* va_end(args); */
+}
+
+void
+_jc_fatal_error_v(_jc_jvm *vm, const char *fmt, va_list args)
+{
+	int (*printer)(FILE *, const char *, va_list);
+	void (*aborter)(void);
+
+	/* Print message */
+	if (vm != NULL) {
+		printer = vm->vfprintf;
+		aborter = vm->abort;
+	} else {
+		printer = vfprintf;
+		aborter = abort;
+	}
+	(*printer)(stderr, "jc: ", args);
+	(*printer)(stderr, fmt, args);
+	(*printer)(stderr, "\n", args);
+
+	/* Die */
+	(*aborter)();
+
+	/* Should never get here */
+	fprintf(stderr, "jc: abort() function returned!\n");
+	abort();
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_final.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_final.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_final.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_final.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: gc_final.c,v 1.3 2005/03/16 15:31:12 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Do a periodic check after scanning this many objects */
+#define _JC_FINALIZE_CHECK		16
+
+/*
+ * Finalize finalizable objects and enqueue enqueable references.
+ */
+jint
+_jc_gc_finalize(_jc_env *env)
+{
+	_jc_jvm *const vm = env->vm;
+	_jc_heap *const heap = &vm->heap;
+	_jc_heap_sweep sweep;
+	_jc_object *obj;
+	int i = 0;
+
+start_over:
+	_jc_heap_sweep_init(heap, &sweep);
+	while ((obj = _jc_heap_sweep_next(&sweep, JNI_FALSE)) != NULL) {
+		_jc_word lockword = obj->lockword;
+		jint gc_cycles;
+		jobject ref;
+
+		/* Sanity check */
+		_JC_ASSERT((lockword & _JC_LW_KEEP_BIT) != 0);
+
+		/* Check for live, enqueable references */
+		if ((lockword & (_JC_LW_SPECIAL_BIT|_JC_LW_LIVE_BIT))
+		      == (_JC_LW_SPECIAL_BIT|_JC_LW_LIVE_BIT)
+		    && _jc_subclass_of(obj, vm->boot.types.Reference)
+		    && *_JC_VMFIELD(vm, obj,
+		      Reference, queue, _jc_object *) != NULL) {
+			_jc_object *referent;
+
+			/* Get referent */
+			referent = *_JC_VMFIELD(vm, obj,
+			    Reference, referent, _jc_object *);
+
+			/* Sanity check: non-null referent should be kept */
+			_JC_ASSERT(referent == NULL
+			    || _JC_LW_TEST(referent->lockword, KEEP));
+
+			/* References are enqueable if the referent is null */
+			if (referent == NULL) {
+
+				/* Add native reference to reference */
+				ref = _jc_new_local_native_ref(env, obj);
+				_JC_ASSERT(ref != NULL);
+
+				/* Invoke enqueue(), ignoring any exception */
+				gc_cycles = vm->gc_cycles;
+				if (_jc_invoke_virtual(env,
+				      vm->boot.methods.Reference.enqueue, obj)
+				    != JNI_OK)
+					_jc_retrieve_exception(env, NULL);
+
+				/* Free local native reference */
+				_jc_free_local_native_ref(&ref);
+
+				/* If a GC cycle happened, start over */
+				if (gc_cycles != vm->gc_cycles)
+					goto start_over;
+
+				/* Sanity check queue field is now null */
+				_JC_ASSERT(*_JC_VMFIELD(vm, obj,
+				    Reference, queue, _jc_object *) == NULL);
+			}
+		}
+
+		/* Check for unreachable but finalizable objects */
+		if ((lockword & (_JC_LW_LIVE_BIT|_JC_LW_FINALIZE_BIT))
+		    != _JC_LW_FINALIZE_BIT)
+			goto next_object;
+
+		/* Add native reference to object */
+		ref = _jc_new_local_native_ref(env, obj);
+		_JC_ASSERT(ref != NULL);
+
+		/* Mark object as no longer finalizable */
+		while (!_jc_compare_and_swap(&obj->lockword,
+		    lockword, lockword & ~_JC_LW_FINALIZE_BIT))
+			lockword = obj->lockword;
+
+		/*
+		 * Finalize object, ignoring any exceptions. Note that a GC
+		 * cycle can happen here; our native reference keeps this
+		 * object (and any objects it references) alive.
+		 */
+		gc_cycles = vm->gc_cycles;
+		if (_jc_invoke_virtual(env,
+		    vm->boot.methods.Object.finalize, obj) != JNI_OK)
+			_jc_retrieve_exception(env, NULL);
+
+		/* Free local native reference */
+		_jc_free_local_native_ref(&ref);
+
+		/*
+		 * If a GC cycle happened, more objects may have become
+		 * finalizable, so start over.
+		 */
+		if (gc_cycles != vm->gc_cycles)
+			goto start_over;
+
+		/* Do a periodic check after finalization */
+		i = -1;
+
+next_object:
+		/* Periodic check */
+		if ((++i % _JC_FINALIZE_CHECK) == 0
+		    && _jc_thread_check(env) != JNI_OK)
+			return JNI_ERR;
+	}
+
+	/* Done */
+	return JNI_OK;
+}
+

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_root.c
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_root.c?rev=294974&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_root.c (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/gc_root.c Tue Oct  4 19:19:16 2005
@@ -0,0 +1,452 @@
+
+/*
+ * Copyright 2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * $Id: gc_root.c,v 1.12 2005/03/20 23:05:31 archiecobbs Exp $
+ */
+
+#include "libjc.h"
+
+/* Internal definitions */
+#define _JC_NUM_REGISTER_OFFS						\
+	(sizeof(_jc_register_offs) / sizeof(*_jc_register_offs))
+
+/* Internal functions */
+static _jc_object	*_jc_locate_object(_jc_jvm *vm,
+				const _jc_word *info, const void *ptr);
+static int		_jc_root_walk_thread(_jc_env *thread,
+				const _jc_word *info, _jc_object ***refsp);
+static int		_jc_root_walk_native_refs(_jc_native_frame_list *list,
+				_jc_object ***refsp);
+static int		_jc_scan_exec_stack(_jc_jvm *vm, _jc_exec_stack *stack,
+				const _jc_word *info, _jc_object ***refsp);
+static int		_jc_scan_interp_stack(_jc_jvm *vm,
+				_jc_interp_stack *stack, const _jc_word *info,
+				_jc_object ***refsp);
+
+/* Internal variables */
+static const int	_jc_register_offs[] = _JC_REGISTER_OFFS;
+
+/*
+ * Find the head of the object given a pointer into its interior.
+ * This is used for conservative GC scan.
+ */
+static _jc_object *
+_jc_locate_object(_jc_jvm *vm, const _jc_word *info, const void *ptr)
+{
+	_jc_heap *const heap = &vm->heap;
+	_jc_word word;
+	char *page;
+	int pnum;
+
+	/* Does pointer point into the heap? */
+	if (!_JC_IN_HEAP(heap, ptr)) {
+		_jc_class_loader *loader;
+
+		/* Handle a common case */
+		if (ptr == NULL)
+			return NULL;
+
+		/* Check each class loader (except boot loader) */
+		LIST_FOREACH(loader, &vm->class_loaders, link) {
+			if (loader == vm->boot.loader)
+				continue;
+			if (_jc_uni_contains(&loader->uni, ptr))
+				return loader->instance;
+		}
+
+		/* Done */
+		return NULL;
+	}
+
+	/* Get page number */
+	pnum = ((char *)ptr - (char *)heap->pages) / _JC_PAGE_SIZE;
+	_JC_ASSERT(pnum >= 0 && pnum < heap->num_pages);
+
+	/* If page is an interior page, find the first page */
+	while ((info[pnum / _JC_BITS_PER_WORD]
+	    & (1 << (pnum % _JC_BITS_PER_WORD))) != 0)
+		pnum--;
+	page = (char *)heap->pages + pnum * _JC_PAGE_SIZE;
+
+	/* Examine page type to find start of object memory block */
+	word = *((_jc_word *)page);
+	switch (_JC_HEAP_EXTRACT(word, PTYPE)) {
+	case _JC_HEAP_PAGE_FREE:
+		return NULL;
+	case _JC_HEAP_PAGE_SMALL:
+	    {
+		char *const first_block = page + _JC_HEAP_BLOCK_OFFSET;
+		_jc_heap_size *size;
+		int block_num;
+
+		/* Get block size in this page */
+		size = &heap->sizes[_JC_HEAP_EXTRACT(word, BSI)];
+
+		/* Find block into which pointer is pointing */
+		block_num = ((char *)ptr - first_block) / size->size;
+		if (block_num < 0 || block_num >= size->num_blocks)
+			return NULL;
+
+		/* Point to start of block */
+		ptr = first_block + block_num * size->size;
+
+		/* See if there really is an object in there */
+		word = *((_jc_word *)ptr);
+		_JC_ASSERT(word != _JC_HEAP_BLOCK_ALLOC);
+		if (word == _JC_HEAP_BLOCK_FREE)
+			return NULL;
+		break;
+	    }
+	case _JC_HEAP_PAGE_LARGE:
+		ptr = page + _JC_HEAP_BLOCK_OFFSET;
+		break;
+	default:
+		_JC_ASSERT(0);
+	}
+
+	/*
+	 * If we could assume all references really pointed to the
+	 * head of the object, then we could optimize further here.
+	 */
+
+	/* Find object head */
+	return _jc_find_object_head(ptr);
+}
+
+/*
+ * Compute the root set of references. Returns the number of references,
+ * or -1 if there was an error. The caller must free the returned array.
+ * Ths stack of the current thread must be clipped.
+ *
+ * If unsuccessful an exception is stored.
+ *
+ * NOTE: This assumes the VM mutex is locked and the world is stopped.
+ */
+int
+_jc_root_walk(_jc_env *env, _jc_object ***refsp)
+{
+	const int nrefs_slop = 32;
+	_jc_jvm *const vm = env->vm;
+	_jc_heap *const heap = &vm->heap;
+	_jc_object **refs_start = NULL;
+	_jc_object **refs = NULL;
+	_jc_env *thread;
+	_jc_word *info;
+	int max_nrefs = 0;
+	int nrefs = 0;
+	int pnum;
+
+	/* Sanity check */
+	_JC_MUTEX_ASSERT(env, vm->mutex);
+	_JC_ASSERT(vm->world_stopped);
+
+	/* Allocate heap page info bit array */
+	if ((info = _jc_vm_zalloc(env, sizeof(_jc_word)
+	    * _JC_HOWMANY(heap->num_pages, _JC_BITS_PER_WORD))) == NULL)
+		return -1;
+
+	/* Mark heap pages which are interior pages of a large object */
+	for (pnum = 0; pnum < heap->num_pages; ) {
+		_jc_word word;
+
+		word = *((_jc_word *)((char *)heap->pages
+		    + pnum * _JC_PAGE_SIZE));
+		switch (_JC_HEAP_EXTRACT(word, PTYPE)) {
+		case _JC_HEAP_PAGE_ALLOC:
+			_JC_ASSERT(0);
+		case _JC_HEAP_PAGE_FREE:
+		case _JC_HEAP_PAGE_SMALL:
+			pnum++;
+			break;
+		case _JC_HEAP_PAGE_LARGE:
+		    {
+			const int npages = _JC_HEAP_EXTRACT(word, NPAGES);
+			int i;
+
+			for (i = pnum + 1; i < pnum + npages; i++) {
+				info[i / _JC_BITS_PER_WORD]
+				    |= 1 << (i % _JC_BITS_PER_WORD);
+			}
+			pnum += npages;
+			break;
+		    }
+		}
+		_JC_ASSERT(pnum <= heap->num_pages);
+	}
+
+again:
+	/* First count the references, then allocate array and record them */
+	nrefs = 0;
+
+	/* Walk threads */
+	LIST_FOREACH(thread, &vm->threads.alive_list, link)
+		nrefs += _jc_root_walk_thread(thread, info, &refs);
+
+	/* Walk global native references */
+	nrefs += _jc_root_walk_native_refs(&vm->native_globals, &refs);
+
+	/*
+	 * After counting references, allocate array and walk them again.
+	 * Because the current thread's stack may change, allow some slop.
+	 */
+	if (refs == NULL) {
+		max_nrefs = nrefs + nrefs_slop;
+		if ((refs = _jc_vm_alloc(env,
+		    max_nrefs * sizeof(*refs))) == NULL) {
+			nrefs = -1;
+			goto done;
+		}
+		refs_start = refs;
+		goto again;
+	}
+
+	/* Sanity check we didn't somehow get a bunch more refs */
+	_JC_ASSERT(nrefs <= max_nrefs);
+
+done:
+	/* Done */
+	_jc_vm_free(&info);
+	*refsp = refs_start;
+	return nrefs;
+}
+
+/*
+ * Walk all objects in one thread's root set. The thread's Java stack
+ * must already be clipped.
+ *
+ * NOTE: This assumes caller has acquired the VM mutex.
+ */
+static int
+_jc_root_walk_thread(_jc_env *thread, const _jc_word *info, _jc_object ***refsp)
+{
+	_jc_jvm *const vm = thread->vm;
+	_jc_object **refs = *refsp;
+	_jc_java_stack *jstack;
+	_jc_stack_crawl crawl;
+	int count = 0;
+
+	/* Sanity check */
+	_JC_ASSERT(thread->java_stack == NULL
+	    || thread->java_stack->interp
+	    || ((_jc_exec_stack *)thread->java_stack)->pc != NULL);
+
+	/* Scan each contiguous Java stack segment */
+	for (jstack = thread->java_stack;
+	    jstack != NULL; jstack = jstack->next) {
+		count += jstack->interp ?
+		    _jc_scan_interp_stack(vm,
+		      (_jc_interp_stack *)jstack, info, &refs) :
+		    _jc_scan_exec_stack(vm,
+		      (_jc_exec_stack *)jstack, info, &refs);
+	}
+
+	/* Get implicit references from Java methods to their classes */
+	for (_jc_stack_crawl_first(thread, &crawl);
+	    crawl.method != NULL; _jc_stack_crawl_next(vm, &crawl)) {
+
+		/* Skip _jc_invoke_jcni_a() */
+		if (crawl.method->class == NULL) {
+			_JC_ASSERT(crawl.method == &vm->invoke_method);
+			continue;
+		}
+
+		/* Add the Class instance */
+		_JC_ASSERT(crawl.method->class->instance != NULL);
+		if (refs != NULL)
+			*refs++ = crawl.method->class->instance;
+		count++;
+	}
+
+	/* Walk thread's local native references */
+	count += _jc_root_walk_native_refs(&thread->native_locals, &refs);
+
+	/* Walk thread's Thread instance (if any) */
+	if (thread->instance != NULL) {
+		if (refs != NULL)
+			*refs++ = thread->instance;
+		count++;
+	}
+
+	/* Walk thread's pending exception (if any) */
+	if (thread->head.pending != NULL) {
+		if (refs != NULL)
+			*refs++ = thread->head.pending;
+		count++;
+	}
+
+	/* Walk any exception posted in this thread by another thread */
+	if (thread->cross_exception != NULL) {
+		if (refs != NULL)
+			*refs++ = thread->cross_exception;
+		count++;
+	}
+
+	/* Done */
+	*refsp = refs;
+	return count;
+}
+
+/*
+ * Scan an interpreter stack frame.
+ */
+static int
+_jc_scan_interp_stack(_jc_jvm *vm, _jc_interp_stack *stack,
+	const _jc_word *info, _jc_object ***refsp)
+{
+	_jc_method *const method = stack->method;
+	_jc_method_code *const code = &method->u.code;
+	_jc_object **refs = *refsp;
+	_jc_object *obj;
+	int count = 0;
+	int i;
+
+	/* Sanity check */
+	_JC_ASSERT(_JC_ACC_TEST(method, INTERP));
+
+	/* Any state in this method yet? */
+	if (stack->locals == NULL)
+		return 0;
+
+	/* Scan stack frame's locals and Java operand stack */
+	for (i = 0; i < code->max_locals + code->max_stack; i++) {
+		_jc_object *const ref = (_jc_object *)stack->locals[i];
+
+		/* Find object pointed to, if any */
+		if ((obj = _jc_locate_object(vm, info, ref)) == NULL)
+			continue;
+
+		/* Add object to list */
+		if (refs != NULL)
+			*refs++ = obj;
+		count++;
+	}
+
+	/* Done */
+	*refsp = refs;
+	return count;
+}
+
+/*
+ * Scan a contiguous executable stack chunk.
+ */
+static int
+_jc_scan_exec_stack(_jc_jvm *vm, _jc_exec_stack *stack,
+	const _jc_word *info, _jc_object ***refsp)
+{
+	const size_t stack_step = (_JC_STACK_ALIGN < sizeof(void *)) ?
+	    _JC_STACK_ALIGN : sizeof(void *);
+	_jc_object **refs = *refsp;
+	const char *stack_bot = NULL;
+	const char *stack_top = NULL;
+	_jc_stack_crawl crawl;
+	_jc_object *obj;
+	const char *ptr;
+	int count = 0;
+	int regnum;
+
+	/* Get references from saved registers */
+	for (regnum = 0; regnum < _JC_NUM_REGISTER_OFFS; regnum++) {
+
+		/* Find object pointed to by register, if any */
+		if ((obj = _jc_locate_object(vm, info,
+		    *(_jc_word **)((char *)&stack->regs
+		      + _jc_register_offs[regnum]))) == NULL)
+			continue;
+
+		/* Add object to list */
+		if (refs != NULL)
+			*refs++ = obj;
+		count++;
+	}
+
+	/* Compute the top of this Java stack segment */
+	stack_top = _jc_mcontext_sp(&stack->regs);
+
+	/* Find the bottom of this Java stack segment */
+	memset(&crawl, 0, sizeof(crawl));
+	crawl.frame = stack->frame;
+	crawl.pc = stack->pc;
+	while (crawl.method != &vm->invoke_method) {
+		_jc_stack_frame_next(&crawl.frame, &crawl.pc);
+		_jc_stack_crawl_skip(vm, &crawl);
+	}
+	stack_bot = _jc_stack_frame_sp(crawl.frame);
+
+	/* Sanity check stack alignment */
+	_JC_ASSERT(((_jc_word)stack_top & (_JC_STACK_ALIGN - 1)) == 0);
+	_JC_ASSERT(((_jc_word)stack_bot & (_JC_STACK_ALIGN - 1)) == 0);
+
+	/* Conservatively find references in this Java stack segment */
+#if _JC_DOWNWARD_STACK
+	for (ptr = stack_top; ptr < stack_bot; ptr += stack_step)
+#else
+	for (ptr = stack_bot; ptr < stack_top; ptr += stack_step)
+#endif
+	{
+		/* Find object pointed into, if any */
+		if ((obj = _jc_locate_object(vm,
+		    info, *(_jc_word **)ptr)) == NULL)
+			continue;
+
+		/* Add object to list */
+		if (refs != NULL)
+			*refs++ = obj;
+		count++;
+	}
+
+	/* Done */
+	*refsp = refs;
+	return count;
+}
+
+/*
+ * Walk all objects in a native reference list.
+ */
+static int
+_jc_root_walk_native_refs(_jc_native_frame_list *list, _jc_object ***refsp)
+{
+	_jc_object **refs = *refsp;
+	_jc_native_frame *frame;
+	int count = 0;
+	int i;
+
+	/* Iterate through each frame */
+	SLIST_FOREACH(frame, list, link) {
+
+		/* Skip empty frames */
+		if (!_JC_NATIVE_REF_ANY_USED(frame))
+			continue;
+
+		/* Iterate references in this frame */
+		for (i = 0; i < _JC_NATIVE_REFS_PER_FRAME; i++) {
+			if (!_JC_NATIVE_REF_IS_FREE(frame, i)) {
+				_jc_object *const obj = frame->refs[i];
+
+				if (obj != NULL) {
+					if (refs != NULL)
+						*refs++ = obj;
+					count++;
+				}
+			}
+		}
+	}
+
+	/* Done */
+	*refsp = refs;
+	return count;
+}
+