/* 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 This file is part of CACAO. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id$ */ #include "config.h" #include #include #include "codegen.h" /* for PATCHER_NOPS */ #include "mm/memory.h" #include "threads/lock-common.h" #include "toolbox/list.h" #include "toolbox/logging.h" /* XXX remove me! */ #include "vm/exceptions.h" #include "vm/vm.h" /* for vm_abort */ #include "vm/jit/code.h" #include "vm/jit/jit.h" #include "vm/jit/patcher-common.h" #include "vmcore/options.h" /* patcher_list_create ********************************************************* TODO *******************************************************************************/ void patcher_list_create(codeinfo *code) { code->patchers = list_create(OFFSET(patchref_t, linkage)); } /* patcher_list_reset ********************************************************** TODO *******************************************************************************/ void patcher_list_reset(codeinfo *code) { patchref_t *pr; /* free all elements of the list */ while((pr = list_first(code->patchers)) != NULL) { list_remove(code->patchers, pr); FREE(pr, patchref_t); #if defined(ENABLE_STATISTICS) if (opt_stat) size_patchref -= sizeof(patchref_t); #endif } } /* patcher_list_free *********************************************************** TODO *******************************************************************************/ void patcher_list_free(codeinfo *code) { /* free all elements of the list */ patcher_list_reset(code); /* free the list itself */ FREE(code->patchers, list_t); } /* patcher_list_find *********************************************************** TODO NOTE: Caller should hold the patcher list lock or maintain exclusive access otherwise. *******************************************************************************/ static patchref_t *patcher_list_find(codeinfo *code, u1 *pc) { patchref_t *pr; /* walk through all patcher references for the given codeinfo */ pr = list_first_unsynced(code->patchers); while (pr) { if (pr->mpc == (ptrint) pc) return pr; pr = list_next_unsynced(code->patchers, pr); } return NULL; } /* patcher_add_patch_ref ******************************************************* Appends a new patcher reference to the list of patching positions. *******************************************************************************/ void patcher_add_patch_ref(jitdata *jd, functionptr patcher, voidptr ref, s4 disp) { codegendata *cd; codeinfo *code; patchref_t *pr; s4 patchmpc; cd = jd->cd; 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) */ pr = NEW(patchref_t); list_add_first_unsynced(code->patchers, pr); #if defined(ENABLE_STATISTICS) if (opt_stat) size_patchref += sizeof(patchref_t); #endif /* set patcher information (mpc is resolved later) */ pr->mpc = patchmpc; pr->disp = disp; pr->patcher = patcher; pr->ref = ref; pr->mcode = 0; pr->done = false; /* Generate NOPs for opt_shownops. */ if (opt_shownops) PATCHER_NOPS; } /* patcher_handler ************************************************************* TODO *******************************************************************************/ /*#define TRACE_PATCHER*/ #ifdef TRACE_PATCHER /* 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; ipatchers); /* search the patcher information for the given PC */ pr = patcher_list_find(code, pc); if (pr == NULL) vm_abort("patcher_handler: Unable to find patcher reference."); if (pr->done) { log_println("patcher_handler: double-patching detected!"); 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); #endif /* cast the passed function to a patcher function */ patcher_function = (bool (*)(patchref_t *)) (ptrint) pr->patcher; /* call the proper 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"); } #endif /* check for return value and exit accordingly */ if (result == false) { e = exceptions_get_and_clear_exception(); LOCK_MONITOR_EXIT(code->patchers); return e; } pr->done = true; /* XXX this is only preliminary to prevent double-patching */ LOCK_MONITOR_EXIT(code->patchers); return NULL; } /* * 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 * Emacs will automagically detect them. * --------------------------------------------------------------------- * Local variables: * mode: c * indent-tabs-mode: t * c-basic-offset: 4 * tab-width: 4 * End: * vim:noexpandtab:sw=4:ts=4: */