Path: blob/master/runtime/bcutil/ComparingCursor.cpp
5985 views
/*******************************************************************************1* Copyright (c) 2001, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21/*22* ComparingCursor.cpp23*/2425#include "ComparingCursor.hpp"2627#include "ClassFileOracle.hpp"28#include "SRPKeyProducer.hpp"29#include "SCQueryFunctions.h"3031#include "ut_j9bcu.h"32#include "bcnames.h"33#include "pcstack.h"34#include "rommeth.h"35#include "vrfytbl.h"36#include "bytecodewalk.h"373839ComparingCursor::ComparingCursor(J9JavaVM *javaVM, SRPOffsetTable *srpOffsetTable,40SRPKeyProducer *srpKeyProducer, ClassFileOracle *classFileOracle, U_8 *romClass,41bool romClassIsShared, ROMClassCreationContext * context, bool isComparingLambdaFromSCC) :42Cursor(0, srpOffsetTable, context),43_javaVM(javaVM),44_checkRangeInSharedCache(romClassIsShared),45_classFileOracle(classFileOracle),46_srpKeyProducer(srpKeyProducer),47_romClass(romClass),48_mode(Cursor::MAIN_CURSOR),49_storePointerToVariableInfo(NULL),50_basePointerToVariableInfo(NULL),51_mainHelper(srpOffsetTable, romClass, context),52_lineNumberHelper(srpOffsetTable, romClass, context),53_varInfoHelper(srpOffsetTable, romClass, context),54_isEqual(true),55_isComparingLambdaFromSCC(isComparingLambdaFromSCC)56{57if (!_checkRangeInSharedCache && (NULL != javaVM)) {58/* Enter mutex in order to safely iterate over the segments in getMaximumValidLengthForPtrInSegment(). */59omrthread_monitor_enter(javaVM->classMemorySegments->segmentMutex);60}6162}6364ComparingCursor::~ComparingCursor()65{66if (!_checkRangeInSharedCache && (NULL != _javaVM)) {67/* Exit mutex that was entered in the constructor. */68omrthread_monitor_exit(_javaVM->classMemorySegments->segmentMutex);69}70}7172UDATA73ComparingCursor::getCount()74{75/* There is no call to getCount for the compare cursor76* that is interested in _lineNumberHelper or _varInfoHelper77* counts.78*/79return _mainHelper.getCount();80}8182void83ComparingCursor::writeU8(U_8 u8Value, DataType dataType)84{85ComparingCursorHelper * countingcursor = getCountingCursor(dataType);86if ( shouldCheckForEquality(dataType) ) {87if ( !isRangeValid(sizeof(U_8), dataType) || (u8Value != *(U_8*)(countingcursor->getBaseAddress() + countingcursor->getCount())) ) {88markUnEqual();89}90}91countingcursor->writeU8(u8Value, dataType);92}9394void95ComparingCursor::writeU16(U_16 u16Value, DataType dataType)96{97ComparingCursorHelper * countingcursor = getCountingCursor(dataType);98if ( shouldCheckForEquality(dataType) ) {99if ( !isRangeValid(sizeof(U_16), dataType) || (u16Value != *(U_16 *)(countingcursor->getBaseAddress() + countingcursor->getCount())) ) {100markUnEqual();101}102}103countingcursor->writeU16(u16Value, dataType);104}105106void107ComparingCursor::writeU32(U_32 u32Value, DataType dataType)108{109ComparingCursorHelper * countingcursor = getCountingCursor(dataType);110if ( shouldCheckForEquality(dataType, u32Value) ) {111U_32 * tmpu32 = (U_32 *)(countingcursor->getBaseAddress() + countingcursor->getCount());112if (!isRangeValid(sizeof(U_32), dataType) || (u32Value != *tmpu32)) {113markUnEqual();114}115}116countingcursor->writeU32(u32Value, dataType);117}118119U_32120ComparingCursor::peekU32(DataType dataType) {121ComparingCursorHelper * countingcursor = getCountingCursor(dataType);122U_32 * tmpptr = (U_32 *)(countingcursor->getBaseAddress() + countingcursor->getCount());123return *tmpptr;124}125126/* If skip is called on a comparing cursor then this function127* must be called to ensure the correct helpers '_count' is128* incremented129*/130void131ComparingCursor::skip(UDATA byteCount, DataType dataType)132{133Cursor * countingcursor = getCountingCursor(dataType);134countingcursor->skip(byteCount);135}136137void138ComparingCursor::writeU64(U_32 u32ValueHigh, U_32 u32ValueLow, DataType dataType)139{140ComparingCursorHelper * countingcursor = getCountingCursor(dataType);141if ( shouldCheckForEquality(dataType) ) {142if ( isRangeValid(sizeof(U_64), dataType) ) {143#ifdef J9VM_ENV_LITTLE_ENDIAN144U_64 u64Value = (U_64(u32ValueLow) << 32) + u32ValueHigh;145#else146U_64 u64Value = (U_64(u32ValueHigh) << 32) + u32ValueLow;147#endif148if ( u64Value != *(U_64 *)(countingcursor->getBaseAddress() + countingcursor->getCount()) ) {149markUnEqual();150}151} else {152markUnEqual();153}154}155countingcursor->writeU64(u32ValueHigh, u32ValueLow, dataType);156}157158void159ComparingCursor::writeSRP(UDATA srpKey, DataType dataType)160{161ComparingCursorHelper * countingcursor = getCountingCursor(dataType);162U_8 * currentAddr = countingcursor->getBaseAddress() + countingcursor->getCount();163if ( shouldCheckForEquality(dataType) ) {164if ( isRangeValid(sizeof(J9SRP), dataType) ) {165switch (dataType) {166case Cursor::SRP_TO_DEBUG_DATA:167case Cursor::SRP_TO_GENERIC:168/* ignore this type */169break;170case Cursor::SRP_TO_LOCAL_VARIABLE_DATA:171{172/* test that the SRP is valid */173void * tmpvardata = SRP_PTR_GET(currentAddr, void *);174if ( NULL == tmpvardata ) {175if (!isSRPNull(srpKey)) {176markUnEqual();177}178} else if ( (_context->shouldPreserveLocalVariablesInfo() && isSRPNull(srpKey)) || !isRangeValidForPtr((U_8 *)tmpvardata,sizeof(void*)) ) {179/* If variable information is not required then this SRP will not be marked during the 'mark & count' phase.180* Checking shouldPreserveLocalVariablesInfo() ensure we isSRPNull() only if it could have been marked.181*/182markUnEqual();183}184break;185}186case Cursor::SRP_TO_SOURCE_DEBUG_EXT: {187/* test that the SRP is valid */188void * debugext = SRP_PTR_GET(currentAddr, void *);189190if ( NULL == debugext ) {191if (!isSRPNull(srpKey)) {192markUnEqual();193}194} else if ( isSRPNull(srpKey) || !isRangeValidForPtr((U_8 *)debugext,sizeof(void*)) ) {195markUnEqual();196}197break;198}199case Cursor::LOCAL_VARIABLE_DATA_SRP_TO_UTF8:200case Cursor::OPTINFO_SOURCE_FILE_NAME:201case Cursor::SRP_TO_UTF8_CLASS_NAME:202case Cursor::SRP_TO_UTF8: {203/* test that the UTF8's are identical */204J9UTF8 * utf8 = SRP_PTR_GET(currentAddr, J9UTF8 *);205206if ( NULL == utf8 ) {207if (!isSRPNull(srpKey)) {208markUnEqual();209}210} else if ( isSRPNull(srpKey) || !isRangeValidForUTF8Ptr(utf8) ) {211markUnEqual();212} else {213U_16 cpIndex = _srpKeyProducer->mapKeyToCfrConstantPoolIndex(srpKey);214215#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)216/**217* cpIndex greater than or equal than the cpCount are injected interfaces, which should not be written into218* the classfile bytes219*/220if ( cpIndex >= _classFileOracle->getConstantPoolCount() ) {221break;222}223#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */224225if ( J9UTF8_LENGTH(utf8) != _classFileOracle->getUTF8Length(cpIndex) ) {226markUnEqual();227} else if (0 != memcmp(J9UTF8_DATA(utf8), _classFileOracle->getUTF8Data(cpIndex), J9UTF8_LENGTH(utf8))) {228markUnEqual();229}230}231break;232}233case Cursor::SRP_TO_NAME_AND_SIGNATURE: {234J9ROMNameAndSignature *nas = SRP_PTR_GET(currentAddr, J9ROMNameAndSignature *);235236if ( NULL == nas ) {237if (!isSRPNull(srpKey)) {238markUnEqual();239}240} else if ( isRangeValidForPtr((U_8*)nas, sizeof(J9ROMNameAndSignature)) ) {241J9UTF8 *name = SRP_GET(nas->name, J9UTF8 *);242J9UTF8 *signature = SRP_GET(nas->signature, J9UTF8 *);243244if ( isSRPNull(srpKey) || !isRangeValidForUTF8Ptr(name) || !isRangeValidForUTF8Ptr(signature) ) {245markUnEqual();246} else {247U_16 cpIndex = _srpKeyProducer->mapKeyToCfrConstantPoolIndex(srpKey);248249if ( (J9UTF8_LENGTH(name) != _classFileOracle->getNameAndSignatureNameUTF8Length(cpIndex)) ||250(J9UTF8_LENGTH(signature) != _classFileOracle->getNameAndSignatureSignatureUTF8Length(cpIndex))251) {252markUnEqual();253} else if ( (0 != memcmp(J9UTF8_DATA(name), _classFileOracle->getNameAndSignatureNameUTF8Data(cpIndex), J9UTF8_LENGTH(name))) ||254(0 != memcmp(J9UTF8_DATA(signature), _classFileOracle->getNameAndSignatureSignatureUTF8Data(cpIndex), J9UTF8_LENGTH(signature)))255) {256markUnEqual();257}258}259} else {260markUnEqual();261}262break;263}264default:265Trc_BCU_Assert_ShouldNeverHappen();266break;267}268} else {269/* Invalid range. */270markUnEqual();271}272}273countingcursor->writeSRP(srpKey, dataType);274}275276void277ComparingCursor::writeWSRP(UDATA srpKey, DataType dataType)278{279Cursor * countingcursor = getCountingCursor(dataType);280if ( shouldCheckForEquality(dataType) ) {281if ( isRangeValid(sizeof(J9WSRP), dataType) ) {282switch (dataType) {283case Cursor::SRP_TO_GENERIC: /* fall through */284case Cursor::SRP_TO_INTERMEDIATE_CLASS_DATA:285/* ignore this type */286break;287default:288Trc_BCU_Assert_ShouldNeverHappen();289break;290}291} else {292markUnEqual();293}294}295countingcursor->writeWSRP(srpKey, dataType);296}297298void299ComparingCursor::writeData(U_8* bytes, UDATA length, DataType dataType)300{301ComparingCursorHelper * countingcursor = getCountingCursor(dataType);302U_8 * baseAddress = countingcursor->getBaseAddress();303U_8 * currentAddr = baseAddress + countingcursor->getCount();304if ( shouldCheckForEquality(dataType) ) {305if ( isRangeValid(length, dataType) ) {306switch ( dataType ) {307case Cursor::INTERMEDIATE_CLASS_DATA: {308J9ROMClass *romClass = (J9ROMClass *)baseAddress;309if (length != (UDATA)romClass->intermediateClassDataLength) {310markUnEqual();311} else {312U_8* intermediates = J9ROMCLASS_INTERMEDIATECLASSDATA(romClass);313if ( 0 != memcmp(bytes, intermediates, length) ) {314markUnEqual();315}316}317break;318}319case Cursor::BYTECODE: {320U_8 *code = bytes;321U_8 *romClassCode = currentAddr;322UDATA codeIndex = 0;323324/*325* An exception must be made for JBgenericReturn bytecodes to allow comparison326* against ROMClasses that have been added to the shared cache. Such ROMClasses327* will have had their return bytecodes "fixed" with fixReturnBytecodes, so any328* of the equivalent return bytecodes is allowed to match an attempt to write a329* JBgenericReturn.330*/331332while (codeIndex < length) {333UDATA startCodeIndex = codeIndex;334U_8 instruction = code[codeIndex];335U_8 romInstruction = romClassCode[codeIndex];336337/* Check if the bytecode matches with a special case for JBgenericReturn. */338if (instruction != romInstruction) {339if ((JBgenericReturn != instruction) ||340(RTV_RETURN != (J9JavaBytecodeVerificationTable[romInstruction] >> 8))341) {342markUnEqual();343break;344}345}346347if (JBtableswitch == instruction) {348codeIndex += 4 - (codeIndex & 3); /* step past instruction + pad */349codeIndex += sizeof(I_32); /* default offset */350I_32 low = *(I_32 *)&code[codeIndex];351codeIndex += sizeof(I_32);352I_32 high = *(I_32 *)&code[codeIndex];353codeIndex += sizeof(I_32);354I_32 noffsets = high - low + 1;355codeIndex += noffsets * sizeof(I_32);356} else if (JBlookupswitch == instruction) {357codeIndex += 4 - (codeIndex & 3); /* step past instruction + pad */358codeIndex += sizeof(I_32); /* default offset */359I_32 npairs = *(I_32 *)&code[codeIndex];360codeIndex += sizeof(I_32);361codeIndex += npairs * 2 * sizeof(I_32);362} else {363UDATA step = (J9JavaInstructionSizeAndBranchActionTable[instruction] & 0x0f);364if (0 == step) {365/* Bad bytecodes? */366markUnEqual();367break;368}369370codeIndex += step;371}372373if (codeIndex > length) {374/* Stepped out of range? */375markUnEqual();376break;377}378379/* Check if the instruction payload matches. */380UDATA payloadLength = codeIndex - startCodeIndex - 1;381if ( (0 != payloadLength) &&382(0 != memcmp(code + startCodeIndex + 1, romClassCode + startCodeIndex + 1, payloadLength))383) {384markUnEqual();385break;386}387}388break;389}390case SRP_TO_INTERMEDIATE_CLASS_DATA:391/* ignore this type */392break;393default:394if ( 0 != memcmp(bytes, currentAddr, length) ) {395markUnEqual();396}397break;398}399}400} else {401if (dataType == LINE_NUMBER_DATA) {402/* If we are here then the line number data needs to be skipped over,403* in this case we use the length for the existing rom class.404*405* The bytes argument is ignored in the call to Cursor::writeData below.406*/407length = _context->romMethodCompressedLineNumbersLength();408}409}410countingcursor->writeData(bytes, length, dataType);411}412413void414ComparingCursor::padToAlignment(UDATA byteAlignment, DataType dataType)415{416Cursor * countingcursor = getCountingCursor(dataType);417if ( shouldCheckForEquality(dataType) ) {418// TODO419}420countingcursor->padToAlignment(byteAlignment, dataType);421}422423bool424ComparingCursor::shouldCheckForEquality(DataType dataType, U_32 u32Value)425{426if ( !isEqual() ) {427return false;428}429430switch (dataType) {431case SRP_TO_UTF8_CLASS_NAME:432if (isComparingLambdaFromSCC()) {433/* if the class is a lambda class don't compare the class names because lambda classes might have different index numbers from run to run */434return false;435}436break;437case BYTECODE: /* fall through */438case GENERIC: /* fall through */439case CLASS_FILE_SIZE: /* fall through */440if ((CLASS_FILE_SIZE == dataType)441&& isComparingLambdaFromSCC()442) {443/* If comparing a lambda class from the shared cache, class file size comparison is already done in ROMClassBuilder::compareROMClassForEquality(). */444return false;445}446case SRP_TO_DEBUG_DATA: /* fall through */447case SRP_TO_GENERIC: /* fall through */448case SRP_TO_UTF8: /* fall through */449case SRP_TO_NAME_AND_SIGNATURE: /* fall through */450case SRP_TO_INTERMEDIATE_CLASS_DATA:451/* do nothing -- return true at bottom of function */452break;453case METHOD_DEBUG_SIZE: /* fall through */454case ROM_SIZE: /* fall through */455case INTERMEDIATE_CLASS_DATA: /* fall through */456case INTERMEDIATE_CLASS_DATA_LENGTH:457return false;458case ROM_CLASS_MODIFIERS:459if (u32Value == (u32Value & _context->romMethodModifiers())) {460/*It's ok if the existing rom class has extra debug information and it is not required*/461return false;462}463break;464case OPTIONAL_FLAGS:465if (u32Value == (u32Value & _context->romClassOptionalFlags())) {466/*During compare it is ok if an existing class has more information*/467return false;468}469break;470case SRP_TO_SOURCE_DEBUG_EXT: /* fall through */471case SOURCE_DEBUG_EXT_LENGTH: /* fall through */472case SOURCE_DEBUG_EXT_DATA:473if (_context->romClassHasSourceDebugExtension() && !(_classFileOracle->hasSourceDebugExtension() && _context->shouldPreserveSourceDebugExtension())) {474/* If the existing rom class contains a source debug extension,475* and it is not required by the new class then skip the comparison.476*/477return false;478}479break;480case OPTINFO_SOURCE_FILE_NAME:481if (_context->romClassHasSourceFileName() && !(_classFileOracle->hasSourceFile() && _context->shouldPreserveSourceFileName())) {482/* If the existing rom class contains a source filename and isn't needed, then skip the comparison.483*/484return false;485}486break;487case SRP_TO_LOCAL_VARIABLE_DATA:488/* fall through489*490* The SRP_TO_LOCAL_VARIABLE_DATA is stored in the header of the method debug data.491*492* If there is no LINE_NUMBER_DATA to be preserved, then the header is also skipped493* and this value is not needed.494*/495case LINE_NUMBER_DATA:496if (!(_context->shouldPreserveLineNumbers()) && _context->romMethodHasDebugData()) {497/* If line number information doesn't need to be preserved, and there is498* existing data then skip the comparison.499*/500return false;501}502break;503case LOCAL_VARIABLE_COUNT: /* fall through */504case LOCAL_VARIABLE_DATA_SRP_TO_UTF8: /* fall through */505case LOCAL_VARIABLE_DATA:506if (!(_context->shouldPreserveLocalVariablesInfo()) && _context->romMethodHasDebugData()) {507/* If variable information doesn't need to be preserved, and there is508* existing data then skip the comparison.509*/510return false;511}512break;513default:514Trc_BCU_Assert_ShouldNeverHappen();515break;516}517518return true;519}520521bool522ComparingCursor::isRangeValid(UDATA length, DataType dataType)523{524ComparingCursorHelper * countingcursor = getCountingCursor(dataType);525526if (countingcursor != &(_mainHelper)) {527if (_checkRangeInSharedCache) {528return j9shr_Query_IsAddressInCache(_javaVM, countingcursor->getBaseAddress() + countingcursor->getCount(), length) ? true : false;529}530return true;531} else {532J9ROMClass * rc = (J9ROMClass *)countingcursor->getBaseAddress();533return (countingcursor->getCount() + length) <= rc->romSize;534}535}536537bool538ComparingCursor::isRangeValidForPtr(U_8 *ptr, UDATA length)539{540if (_checkRangeInSharedCache) {541return (j9shr_Query_IsAddressInCache(_javaVM, ptr, length) ? true : false);542} else {543return length < getMaximumValidLengthForPtrInSegment(ptr);544}545}546547bool548ComparingCursor::isRangeValidForUTF8Ptr(J9UTF8 *utf8)549{550U_8 *ptr = (U_8*)utf8;551/*552* Need to check the UTF8 to verify that it is either in a J9MemorySegment or in the553* SCC.554*/555if (_checkRangeInSharedCache) {556/* Need to check if the header (length field) is in range first, before reading the length557* to determine if the rest of the data is in range. Failure to do so results in potentially558* dereferencing inaccessible memory.559*/560return j9shr_Query_IsAddressInCache(_javaVM, utf8, sizeof(J9UTF8))561&& j9shr_Query_IsAddressInCache(_javaVM, utf8, J9UTF8_TOTAL_SIZE(utf8));562} else {563UDATA maxLength = getMaximumValidLengthForPtrInSegment(ptr);564565return J9UTF8_TOTAL_SIZE(utf8) < maxLength;566}567}568569UDATA570ComparingCursor::getMaximumValidLengthForPtrInSegment(U_8 *ptr)571{572Trc_BCU_Assert_False(_checkRangeInSharedCache);573574if (NULL != _javaVM) {575/* There is an AVL tree of class memory segments, use it to efficiently find a potential segment that576* could contain the pointer value. */577J9MemorySegment *segment = (J9MemorySegment *) avl_search(&(_javaVM->classMemorySegments->avlTreeData), UDATA(ptr));578if ( (NULL != segment) && (ptr >= segment->heapBase) && (ptr < segment->heapTop) ) {579return UDATA(segment->heapTop) - UDATA(ptr);580}581582return 0;583}584585/* If _checkRangeInSharedCache is false and _javaVM is NULL, don't validate address ranges. */586return UDATA_MAX;587}588589void590ComparingCursor::notifyDebugDataWriteStart() {591/*Inform the context that _romClass should be used to determine if debug data is inline or out of line*/592_context->startDebugCompare();593/* This method notifies the cursor that writes to the method debug data is about to occur.594* The notification occurs just before deciding to write the SRP to debug area595*/596if (!_context->romMethodDebugDataIsInline()) {597/* If the debug data is out of line, then call resetBaseMemoryLocation to598* point the compare helpers to the correct memory.599*600* Calling resetBaseMemoryLocation will also set _count on the helpers601* to zero since they are indexing a different memory offset.602*/603U_8 * lineNumberStartAddr = (U_8 *) _context->romMethodLineNumbersDataStart();604U_8 * varTableStartAddr = (U_8 *) _context->romMethodVariableDataStart();605_lineNumberHelper.resetBaseMemoryLocation(lineNumberStartAddr);606_varInfoHelper.resetBaseMemoryLocation(varTableStartAddr);607}608}609610void611ComparingCursor::notifyVariableTableWriteEnd() {612if (_context->shouldWriteDebugDataInline() && !(_context->shouldPreserveLocalVariablesInfo())) {613/* This method is required to skip over inline variable table data in an existing rom class during compare.614* This is only done if the variable table is not required by the new class, and it is inline.615*/616J9MethodDebugInfo * debugDataStart = (J9MethodDebugInfo *) _context->romMethodLineNumbersDataStart();617void * variableTableStart = _context->romMethodVariableDataStart();618if (NULL != variableTableStart) {619/*Note: if variableTableStart is null then the variable count is 0 for the existing method*/620U_32 existingClassDebugInfoSize = (debugDataStart->srpToVarInfo & ~0x1);621U_32 sizeWithoutVariableTable = (U_32)((UDATA)variableTableStart - (UDATA)debugDataStart);622U_32 inlineVarInfoToSkip = (existingClassDebugInfoSize - sizeWithoutVariableTable);623/*The debug data is inline so increment the count of _mainHelper*/624_mainHelper.skip(inlineVarInfoToSkip);625}626}627}628629ComparingCursorHelper *630ComparingCursor::getCountingCursor(DataType dataType) {631switch (dataType) {632case LOCAL_VARIABLE_COUNT:633case SRP_TO_LOCAL_VARIABLE_DATA:634case LINE_NUMBER_DATA:635if (!(_context->shouldWriteDebugDataInline())) {636return &_lineNumberHelper;637638}639/*If the debug data is inline return _mainHelper*/640break;641case LOCAL_VARIABLE_DATA_SRP_TO_UTF8:642case LOCAL_VARIABLE_DATA:643if (!(_context->shouldWriteDebugDataInline())) {644return &_varInfoHelper;645}646/*If the debug data is inline return _mainHelper*/647break;648default:649break;650}651return &_mainHelper;652}653654655