/* src/vm/jit/patcher-common.c - architecture independent code patching stuff
- Copyright (C) 2007 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
+ Copyright (C) 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- $Id$
-
*/
#include <stdint.h>
#include "codegen.h" /* for PATCHER_NOPS */
+#include "md.h"
#include "mm/memory.h"
+#include "native/native.h"
+
#include "threads/lock-common.h"
#include "toolbox/list.h"
#include "toolbox/logging.h" /* XXX remove me! */
#include "vm/exceptions.h"
+#include "vm/initialize.h"
+#include "vm/resolve.h"
#include "vm/vm.h" /* for vm_abort */
#include "vm/jit/code.h"
+#include "vm/jit/disass.h"
#include "vm/jit/jit.h"
#include "vm/jit/patcher-common.h"
#include "vmcore/options.h"
+/* patcher_function_list *******************************************************
+
+ This is a list which maps patcher function pointers to the according
+ names of the patcher functions. It is only usefull for debugging
+ purposes.
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+typedef struct patcher_function_list_t {
+ functionptr patcher;
+ char *name;
+} patcher_function_list_t;
+
+static patcher_function_list_t patcher_function_list[] = {
+ { PATCHER_initialize_class, "initialize_class" },
+ { PATCHER_resolve_class, "resolve_class" },
+ { PATCHER_resolve_native_function, "resolve_native_function" },
+ { PATCHER_invokestatic_special, "invokestatic_special" },
+ { PATCHER_invokevirtual, "invokevirtual" },
+ { PATCHER_invokeinterface, "invokeinterface" },
+ { NULL, "-UNKNOWN PATCHER FUNCTION-" }
+};
+#endif
+
+
/* patcher_list_create *********************************************************
- TODO
+ Creates an empty patcher list for the given codeinfo.
*******************************************************************************/
/* patcher_list_reset **********************************************************
- TODO
+ Resets the patcher list inside a codeinfo. This is usefull when
+ resetting a codeinfo for recompiling.
*******************************************************************************/
/* patcher_list_free ***********************************************************
- TODO
+ Frees the patcher list and all its entries for the given codeinfo.
*******************************************************************************/
/* patcher_list_find ***********************************************************
- TODO
+ Find an entry inside the patcher list for the given codeinfo
+ by specifying the program counter of the patcher position.
NOTE: Caller should hold the patcher list lock or maintain
exclusive access otherwise.
/* walk through all patcher references for the given codeinfo */
- pr = list_first_unsynced(code->patchers);
+ pr = list_first(code->patchers);
+
while (pr) {
+/*#define TRACE_PATCHER_FIND*/
+#ifdef TRACE_PATCHER_FIND
+ log_println("patcher_list_find: %p == %p", pr->mpc, pc);
+#endif
+
if (pr->mpc == (ptrint) pc)
return pr;
- pr = list_next_unsynced(code->patchers, pr);
+ pr = list_next(code->patchers, pr);
}
return NULL;
s4 disp)
{
codegendata *cd;
- codeinfo *code;
- patchref_t *pr;
- s4 patchmpc;
+ codeinfo *code;
+ patchref_t *pr;
+ s4 patchmpc;
cd = jd->cd;
- code = jd->code;
- patchmpc = cd->mcodeptr - cd->mcodebase;
+ code = jd->code;
+ patchmpc = cd->mcodeptr - cd->mcodebase;
#if !defined(NDEBUG)
if (patcher_list_find(code, (u1 *) (intptr_t) patchmpc) != NULL)
vm_abort("patcher_add_patch_ref: different patchers at same position.");
#endif
- /* allocate patchref on heap (at least freed together with codeinfo) */
+ /* allocate patchref on heap (at least freed together with codeinfo) */
pr = NEW(patchref_t);
- list_add_first_unsynced(code->patchers, pr);
+ list_add_first(code->patchers, pr);
#if defined(ENABLE_STATISTICS)
if (opt_stat)
size_patchref += sizeof(patchref_t);
#endif
- /* set patcher information (mpc is resolved later) */
+ /* set patcher information (mpc is resolved later) */
- pr->mpc = patchmpc;
- pr->disp = disp;
- pr->patcher = patcher;
- pr->ref = ref;
+ pr->mpc = patchmpc;
+ pr->disp = disp;
+ pr->patcher = patcher;
+ pr->ref = ref;
pr->mcode = 0;
pr->done = false;
- /* Generate NOPs for opt_shownops. */
+#if defined(ENABLE_JITCACHE)
+ pr->attached_ref = NULL;
+#endif
+
+ /* Generate NOPs for opt_shownops. */
+
+ if (opt_shownops)
+ PATCHER_NOPS;
+
+#if defined(ENABLE_JIT) && (defined(__I386__) || defined(__M68K__) || defined(__SPARC_64__) || defined(__X86_64__))
+
+ /* XXX We can remove that when we don't use UD2 anymore on i386
+ and x86_64. */
+
+ /* On some architectures the patcher stub call instruction might
+ be longer than the actual instruction generated. On this
+ architectures we store the last patcher call position and after
+ the basic block code generation is completed, we check the
+ range and maybe generate some nop's. */
+ /* The nops are generated in codegen_emit in each codegen */
+
+ cd->lastmcodeptr = cd->mcodeptr + PATCHER_CALL_SIZE;
+#endif
+}
+
+
+/**
+ * Resolve all patchers in the current JIT run.
+ *
+ * @param jd JIT data-structure
+ */
+void patcher_resolve(jitdata* jd)
+{
+ codeinfo* code;
+ patchref_t* pr;
+
+ /* Get required compiler data. */
- if (opt_shownops)
- PATCHER_NOPS;
+ code = jd->code;
+
+ for (pr = list_first(code->patchers); pr != NULL; pr = list_next(code->patchers, pr)) {
+ pr->mpc += (intptr_t) code->entrypoint;
+ pr->datap = (intptr_t) (pr->disp + code->entrypoint);
+ }
}
/* patcher_handler *************************************************************
- TODO
+ Handles the request to patch JIT code at the given patching
+ position. This function is normally called by the signal
+ handler.
-*******************************************************************************/
+ NOTE: The patcher list lock is used to maintain exclusive
+ access of the patched position (in fact of the whole code).
+ After patching has suceeded, the patcher reference should be
+ removed from the patcher list to avoid double patching.
-/*#define TRACE_PATCHER*/
+*******************************************************************************/
-#ifdef TRACE_PATCHER
+#if !defined(NDEBUG)
/* XXX this indent is not thread safe! */
/* XXX if you want it thread safe, place patcher_depth in threadobject! */
static int patcher_depth = 0;
-# define TRACE_PATCHER_INDENT for (i=0; i<patcher_depth; i++) printf("\t")
-#endif
+#define TRACE_PATCHER_INDENT for (i=0; i<patcher_depth; i++) printf("\t")
+#endif /* !defined(NDEBUG) */
-java_objectheader *patcher_handler(u1 *pc)
+java_handle_t *patcher_handler(u1 *pc)
{
- codeinfo *code;
- patchref_t *pr;
- bool result;
- java_objectheader *e;
-#ifdef TRACE_PATCHER
- int i;
+ codeinfo *code;
+ patchref_t *pr;
+ bool result;
+ java_handle_t *e;
+#if !defined(NDEBUG)
+ patcher_function_list_t *l;
+ int i;
#endif
/* define the patcher function */
vm_abort("patcher_handler: Unable to find patcher reference.");
if (pr->done) {
- log_println("patcher_handler: double-patching detected!");
+#if !defined(NDEBUG)
+ if (opt_DebugPatcher) {
+ log_println("patcher_handler: double-patching detected!");
+ }
+#endif
LOCK_MONITOR_EXIT(code->patchers);
return NULL;
}
-#ifdef TRACE_PATCHER
- TRACE_PATCHER_INDENT; printf("patching in "); method_print(code->m); printf("\n");
- TRACE_PATCHER_INDENT; printf("\texception program counter = %p\n", (void *) pr->mpc);
- TRACE_PATCHER_INDENT; printf("\tmcodes before = "); for (i=0; i<5; i++) printf("0x%08x ", *((u4 *) pr->mpc + i)); printf("\n");
- patcher_depth++;
- assert(patcher_depth > 0);
+#if !defined(NDEBUG)
+ if (opt_DebugPatcher) {
+ for (l = patcher_function_list; l->patcher != NULL; l++)
+ if (l->patcher == pr->patcher)
+ break;
+
+ TRACE_PATCHER_INDENT; printf("patching in "); method_print(code->m); printf(" at %p\n", (void *) pr->mpc);
+ TRACE_PATCHER_INDENT; printf("\tpatcher function = %s <%p>\n", l->name, (void *) (intptr_t) pr->patcher);
+
+ TRACE_PATCHER_INDENT;
+ printf("\tmachine code before = ");
+
+# if defined(ENABLE_DISASSEMBLER)
+ disassinstr((void *) pr->mpc);
+# else
+ printf("disassembler disabled\n");
+# endif
+
+ patcher_depth++;
+ assert(patcher_depth > 0);
+ }
#endif
/* cast the passed function to a patcher function */
result = (patcher_function)(pr);
-#ifdef TRACE_PATCHER
- assert(patcher_depth > 0);
- patcher_depth--;
- TRACE_PATCHER_INDENT; printf("\tmcodes after = "); for (i=0; i<5; i++) printf("0x%08x ", *((u4 *) pr->mpc + i)); printf("\n");
- if (result == false) {
- TRACE_PATCHER_INDENT; printf("\tPATCHER EXCEPTION!\n");
+#if !defined(NDEBUG)
+ if (opt_DebugPatcher) {
+ assert(patcher_depth > 0);
+ patcher_depth--;
+
+ TRACE_PATCHER_INDENT;
+ printf("\tmachine code after = ");
+
+# if defined(ENABLE_DISASSEMBLER)
+ disassinstr((void *) pr->mpc);
+# else
+ printf("disassembler disabled\n");
+# endif
+
+ if (result == false) {
+ TRACE_PATCHER_INDENT; printf("\tPATCHER EXCEPTION!\n");
+ }
+ }
+#endif
+
+#if defined(ENABLE_JITCACHE)
+ /* Put cached reference into the code and remove it from the patcher */
+ if (pr->attached_ref)
+ {
+ jitcache_handle_cached_ref(pr->attached_ref, code);
+ pr->attached_ref = NULL;
}
#endif
}
+/* patcher_initialize_class ****************************************************
+
+ Initalizes a given classinfo pointer.
+ This function does not patch any data.
+
+*******************************************************************************/
+
+bool patcher_initialize_class(patchref_t *pr)
+{
+ classinfo *c;
+
+ /* get stuff from the patcher reference */
+
+ c = (classinfo *) pr->ref;
+
+ /* check if the class is initialized */
+
+ if (!(c->state & CLASS_INITIALIZED))
+ if (!initialize_class(c))
+ return false;
+
+ /* patch back original code */
+
+ patcher_patch_code(pr);
+
+ return true;
+}
+
+
+/* patcher_resolve_class *******************************************************
+
+ Resolves a given unresolved class reference.
+ This function does not patch any data.
+
+*******************************************************************************/
+
+#ifdef ENABLE_VERIFIER
+bool patcher_resolve_class(patchref_t *pr)
+{
+ unresolved_class *uc;
+
+ /* get stuff from the patcher reference */
+
+ uc = (unresolved_class *) pr->ref;
+
+ /* resolve the class and check subtype constraints */
+
+ if (!resolve_class_eager_no_access_check(uc))
+ return false;
+
+ /* patch back original code */
+
+ patcher_patch_code(pr);
+
+ return true;
+}
+#endif /* ENABLE_VERIFIER */
+
+
+/* patcher_resolve_native_function *********************************************
+
+ Resolves the native function for a given methodinfo.
+ This function patches one data segment word.
+
+*******************************************************************************/
+
+bool patcher_resolve_native_function(patchref_t *pr)
+{
+ methodinfo *m;
+ uint8_t *datap;
+ functionptr f;
+
+ /* get stuff from the patcher reference */
+
+ m = (methodinfo *) pr->ref;
+ datap = (uint8_t *) pr->datap;
+
+ /* resolve native function */
+
+ if (!(f = native_method_resolve(m)))
+ return false;
+
+ /* patch native function pointer */
+
+ *((intptr_t *) datap) = (intptr_t) f;
+
+ /* synchronize data cache */
+
+ md_dcacheflush(datap, SIZEOF_VOID_P);
+
+ /* patch back original code */
+
+ patcher_patch_code(pr);
+
+ return true;
+}
+
+/** Placeholder functions to calm down linker */
+#if defined(__I386__)
+bool patcher_resolve_classref_to_classinfo(patchref_t *pr)
+{
+ return true;
+}
+
+bool patcher_resolve_classref_to_vftbl(patchref_t *pr)
+{
+ return true;
+}
+
+bool patcher_resolve_classref_to_index(patchref_t *pr)
+{
+ return true;
+}
+
+bool patcher_resolve_classref_to_flags(patchref_t *pr)
+{
+ return true;
+}
+#endif
+
/*
* These are local overrides for various environment variables in Emacs.
* Please do not remove this and leave it at the end of the file, where