/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.code; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.compiler.CallEntryPoint.*; import com.sun.max.lang.*; import com.sun.max.unsafe.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.compiler.target.amd64.*; import com.sun.max.vm.heap.debug.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.stack.*; import com.sun.max.vm.thread.*; /** * Check all dispatch table entries, direct call sites, and return addresses. * They must be in either of the code regions (but not in baseline from-space), * and dispatch table entries and direct call sites must refer to valid entry points. */ public final class CodeCacheValidation extends VmOperation { public static final CodeCacheValidation instance = new CodeCacheValidation(); final class StackValidator extends RawStackFrameVisitor { @Override public boolean visitFrame(StackFrameCursor current, StackFrameCursor callee) { final TargetMethod tm = callee.targetMethod(); if (tm == null) { return true; } final Pointer rap = tm.returnAddressPointer(callee); final CodePointer ret = CodePointer.from(rap.readWord(0)); // native callee? if (current.targetMethod() == null && !current.sp().isZero()) { return true; } assert validCodeAddress(ret) : "invalid return address in " + tm + " -> " + ret.to0xHexString(); return true; } } final class DirectCallValidator implements TargetMethod.Closure { @Override public boolean doTargetMethod(TargetMethod targetMethod) { validateDirectCallsOf(targetMethod); return true; } } final class DispatchTableValidator implements ClassActor.Closure { @Override public boolean doClass(ClassActor classActor) { final DynamicHub dhub = classActor.dynamicHub(); if (dhub != null) { validateHub(dhub, DynamicHub.vTableStartIndex(), true); } final StaticHub shub = classActor.staticHub(); if (shub != null) { validateHub(shub, Hub.vTableStartIndex(), !(classActor.isInterface() || classActor.isPrimitiveClassActor())); } validateMethods(classActor.localStaticMethodActors()); validateMethods(classActor.localVirtualMethodActors()); return true; } private void validateMethods(MethodActor[] methods) { for (MethodActor ma : methods) { if (ma instanceof ClassMethodActor) { final TargetMethod tm = ((ClassMethodActor) ma).currentTargetMethod(); if (tm != null) { assert validCodeAddress(tm.codeStart()) : "invalid method code start: " + tm + "@" + tm.codeStart().to0xHexString(); } } } } } final class TargetStateValidator implements TargetMethod.Closure { @Override public boolean doTargetMethod(TargetMethod targetMethod) { if (CodeManager.runtimeBaselineCodeRegion.isInToSpace(targetMethod.codeStart().toAddress())) { final ClassMethodActor cma = targetMethod.classMethodActor; assert cma != null : "class method actor null for " + targetMethod; if (!targetMethod.isProtected()) { // target state for a protected method is not yet initialised assert cma.compiledState != Compilations.EMPTY : "target state null for " + targetMethod; final TargetMethod tm = cma.currentTargetMethod(); assert tm != null : "current target method null for " + targetMethod + " (via class method actor " + cma + ")"; final CodePointer cs = tm.codeStart(); assert validCodeAddress(cs) : "target state not referencing to-space for " + targetMethod + ": " + cs.to0xHexString(); } } else { assert targetMethod.classMethodActor.compiledState == Compilations.EMPTY : "target state SHOULD BE null for " + targetMethod; } return true; } } private class TargetAddressesValidator implements TargetMethod.Closure { public Address lastAddress; @Override public boolean doTargetMethod(TargetMethod targetMethod) { assert targetMethod.start().greaterThan(lastAddress) : "method order violation: " + targetMethod.start().to0xHexString() + " must be greater than " + lastAddress.to0xHexString(); lastAddress = targetMethod.start(); final Address tmDataStart = DebugHeap.adjustForDebugTag(targetMethod.start().asPointer()); final Address tmRefLits = Reference.fromJava(targetMethod.referenceLiterals()).toOrigin().asAddress(); assert tmDataStart.equals(tmRefLits) : "reference literals not at method start for " + targetMethod + " should: " + tmDataStart.to0xHexString() + " is: " + tmRefLits.to0xHexString(); CodePointer tmCodeStart = targetMethod.codeStart(); final Offset tmCodeSize = Offset.fromLong(targetMethod.codeLength()); final Address tmUpperBound = tmDataStart.plus(targetMethod.size()).minus(1); CodePointer tmCodeEnd = tmCodeStart.plus(tmCodeSize).minus(1); assert tmCodeEnd.toAddress().lessEqual(tmUpperBound) : "code exceeds upper bound for " + targetMethod + " code start: " + tmCodeStart.to0xHexString() + " size: " + tmCodeSize.to0xHexString() + " ends at: " + tmCodeEnd.to0xHexString() + " exceeds: " + tmUpperBound.to0xHexString(); return true; } } private CodeCacheValidation() { super("code cache validation", null, Mode.Safepoint); } /** * Validates the machine code of a single target method. * This only regards direct calls in the method's machine code. */ public boolean validateSingleMethod(final TargetMethod tm) { validateDirectCallsOf(tm); return true; } @Override protected void doIt() { validateDirectCalls(); validateDispatchTableEntries(); validateTargetStates(); validateTargetMethodAddresses(); doAllThreads(); } @Override protected void doThread(VmThread vmThread, Pointer ip, Pointer sp, Pointer fp) { if (ip.isZero() && sp.isZero() && fp.isZero()) { return; } sfw.setTLA(vmThread.tla()); sfw.inspect(ip, sp, fp, stackValidator); } private final StackValidator stackValidator = new StackValidator(); private final VmStackFrameWalker sfw = new VmStackFrameWalker(Pointer.zero()); private final DirectCallValidator directCallValidator = new DirectCallValidator(); private DispatchTableValidator dispatchTableValidator = new DispatchTableValidator(); private final TargetStateValidator targetStateValidator = new TargetStateValidator(); private final TargetAddressesValidator targetAddressesValidator = new TargetAddressesValidator(); private void validateDirectCalls() { CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(directCallValidator); CodeManager.runtimeOptCodeRegion.doAllTargetMethods(directCallValidator); Code.bootCodeRegion().doAllTargetMethods(directCallValidator); } private void validateDispatchTableEntries() { ClassActor.allClassesDo(dispatchTableValidator); } private void validateTargetStates() { CodeManager.runtimeBaselineCodeRegion.doAllTargetMethods(targetStateValidator); } private void validateTargetMethodAddresses() { targetAddressesValidator.lastAddress = Address.zero(); CodeManager.runtimeBaselineCodeRegion.doNewTargetMethods(targetAddressesValidator); } private boolean validCodeAddress(CodePointer cp) { return Code.contains(cp.toAddress()); } private void validateDirectCallsOf(TargetMethod targetMethod) { if (targetMethod.classMethodActor != null && targetMethod.classMethodActor.isTemplate()) { return; } final Safepoints safepoints = targetMethod.safepoints(); for (int spi = safepoints.nextDirectCall(0); spi >= 0; spi = safepoints.nextDirectCall(spi + 1)) { final int callPos = safepoints.causePosAt(spi); if (platform().isa == ISA.AMD64) { final CodePointer callTarget = AMD64TargetMethodUtil.readCall32Target(targetMethod, callPos); final TargetMethod actualCallee = callTarget.toTargetMethod(); assert validCodeAddress(callTarget) : "invalid call target (address) in direct call from " + targetMethod + "@" + spi + "(pos " + callPos + ") -> " + actualCallee + " (target: " + callTarget.to0xHexString() + ")"; assert actualCallee != null && validEntryPoint(callTarget, actualCallee) : "invalid entry point in direct call from " + targetMethod + "@" + spi + " -> " + actualCallee + " (target: " + callTarget.to0xHexString() + ")"; } else { throw FatalError.unimplemented(); } } } private void validateHub(final Hub hub, final int vstart, final boolean checkVtable) { if (checkVtable) { // vtable entries - this is not necessary for static hubs describing interfaces or primitive types final int vend = vstart + hub.vTableLength(); for (int i = vstart; i < vend; ++i) { final CodePointer address = CodePointer.from(hub.getWord(i)); assert validCodeAddress(address) : "invalid code address in vtable entry: " + hub + "@" + i + " (vt@" + (i - vstart) + ") -> " + address.toString(); } } // itable entries final int istart = hub.iTableStartIndex; final int iend = istart + hub.iTableLength; for (int i = istart + 1; i < iend; ++i) { // start iterating at istart+1 because the first entry is null final CodePointer p = CodePointer.from(hub.getWord(i)); assert p.isZero() // itable entries can be zero || Hub.validItableEntry(p) // or they can represent a class ID || validCodeAddress(p) // or they can point to code : "invalid itable entry: " + hub + "@" + i + " (it@" + (i - istart) + ") -> " + p.toString(); } } private boolean validEntryPoint(CodePointer a, TargetMethod tm) { return a.equals(tm.getEntryPoint(BASELINE_ENTRY_POINT)) || a.equals(tm.getEntryPoint(OPTIMIZED_ENTRY_POINT)) || a.equals(tm.getEntryPoint(VTABLE_ENTRY_POINT)) || a.equals(tm.getEntryPoint(C_ENTRY_POINT)); } }