mirror of
https://github.com/ZuneDev/dotnet-runtime.git
synced 2026-07-27 13:22:01 -07:00
These were from NGEN support, so we can remove the flags, load levels, and assorted checks for them.
444 lines
12 KiB
C++
444 lines
12 KiB
C++
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
#include "common.h"
|
|
#include "versionresilienthashcode.h"
|
|
#include "typehashingalgorithms.h"
|
|
#include "openum.h"
|
|
|
|
bool GetVersionResilientTypeHashCode(IMDInternalImport *pMDImport, mdExportedType token, int * pdwHashCode)
|
|
{
|
|
CONTRACTL
|
|
{
|
|
NOTHROW;
|
|
GC_NOTRIGGER;
|
|
MODE_ANY;
|
|
PRECONDITION(CheckPointer(pdwHashCode));
|
|
}
|
|
CONTRACTL_END
|
|
|
|
_ASSERTE(TypeFromToken(token) == mdtTypeDef ||
|
|
TypeFromToken(token) == mdtTypeRef ||
|
|
TypeFromToken(token) == mdtExportedType);
|
|
_ASSERTE(!IsNilToken(token));
|
|
|
|
HRESULT hr;
|
|
LPCUTF8 szNamespace;
|
|
LPCUTF8 szName;
|
|
bool hasTypeToken = true;
|
|
int hashcode = 0;
|
|
|
|
while (hasTypeToken)
|
|
{
|
|
if (IsNilToken(token))
|
|
return false;
|
|
|
|
switch (TypeFromToken(token))
|
|
{
|
|
case mdtTypeDef:
|
|
if (FAILED(pMDImport->GetNameOfTypeDef(token, &szName, &szNamespace)))
|
|
return false;
|
|
hr = pMDImport->GetNestedClassProps(token, &token);
|
|
if (hr == CLDB_E_RECORD_NOTFOUND)
|
|
hasTypeToken = false;
|
|
else if (FAILED(hr))
|
|
return false;
|
|
break;
|
|
|
|
case mdtTypeRef:
|
|
if (FAILED(pMDImport->GetNameOfTypeRef(token, &szNamespace, &szName)))
|
|
return false;
|
|
if (FAILED(pMDImport->GetResolutionScopeOfTypeRef(token, &token)))
|
|
return false;
|
|
hasTypeToken = (TypeFromToken(token) == mdtTypeRef);
|
|
break;
|
|
|
|
case mdtExportedType:
|
|
if (FAILED(pMDImport->GetExportedTypeProps(token, &szNamespace, &szName, &token, NULL, NULL)))
|
|
return false;
|
|
hasTypeToken = (TypeFromToken(token) == mdtExportedType);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
hashcode ^= ComputeNameHashCode(szNamespace, szName);
|
|
}
|
|
|
|
*pdwHashCode = hashcode;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetVersionResilientMethodDefHashCode(IMDInternalImport *pMDImport, mdMethodDef token, int * pdwHashCode)
|
|
{
|
|
CONTRACTL
|
|
{
|
|
NOTHROW;
|
|
GC_NOTRIGGER;
|
|
MODE_ANY;
|
|
PRECONDITION(CheckPointer(pdwHashCode));
|
|
}
|
|
CONTRACTL_END
|
|
|
|
_ASSERTE(TypeFromToken(token) == mdtMethodDef);
|
|
_ASSERTE(!IsNilToken(token));
|
|
|
|
LPCUTF8 szName;
|
|
|
|
if (FAILED(pMDImport->GetNameOfMethodDef(token, &szName)))
|
|
return false;
|
|
|
|
mdTypeDef tkTypeDef;
|
|
|
|
if (FAILED(pMDImport->GetParentToken(token, &tkTypeDef)))
|
|
return false;
|
|
|
|
int hashCode;
|
|
if (!GetVersionResilientTypeHashCode(pMDImport, tkTypeDef, &hashCode))
|
|
return false;
|
|
|
|
hashCode ^= ComputeNameHashCode(szName);
|
|
|
|
*pdwHashCode = hashCode;
|
|
return true;
|
|
}
|
|
|
|
#ifndef DACCESS_COMPILE
|
|
int GetVersionResilientTypeHashCode(TypeHandle type)
|
|
{
|
|
STANDARD_VM_CONTRACT;
|
|
|
|
if (type.IsArray())
|
|
{
|
|
return ComputeArrayTypeHashCode(GetVersionResilientTypeHashCode(type.GetArrayElementTypeHandle()), type.GetRank());
|
|
}
|
|
else
|
|
if (!type.IsTypeDesc())
|
|
{
|
|
MethodTable *pMT = type.AsMethodTable();
|
|
|
|
_ASSERTE(!pMT->IsArray());
|
|
_ASSERTE(!IsNilToken(pMT->GetCl()));
|
|
|
|
LPCUTF8 szNamespace;
|
|
LPCUTF8 szName;
|
|
IfFailThrow(pMT->GetMDImport()->GetNameOfTypeDef(pMT->GetCl(), &szName, &szNamespace));
|
|
int hashcode = ComputeNameHashCode(szNamespace, szName);
|
|
|
|
MethodTable *pMTEnclosing = pMT->LoadEnclosingMethodTable(CLASS_LOAD_APPROXPARENTS);
|
|
if (pMTEnclosing != NULL)
|
|
{
|
|
hashcode = ComputeNestedTypeHashCode(GetVersionResilientTypeHashCode(TypeHandle(pMTEnclosing)), hashcode);
|
|
}
|
|
|
|
if (!pMT->IsGenericTypeDefinition() && pMT->HasInstantiation())
|
|
{
|
|
return ComputeGenericInstanceHashCode(hashcode,
|
|
pMT->GetInstantiation().GetNumArgs(), pMT->GetInstantiation(), GetVersionResilientTypeHashCode);
|
|
}
|
|
else
|
|
{
|
|
return hashcode;
|
|
}
|
|
}
|
|
else
|
|
if (type.IsPointer())
|
|
{
|
|
return ComputePointerTypeHashCode(GetVersionResilientTypeHashCode(type.AsTypeDesc()->GetTypeParam()));
|
|
}
|
|
else
|
|
if (type.IsByRef())
|
|
{
|
|
return ComputeByrefTypeHashCode(GetVersionResilientTypeHashCode(type.AsTypeDesc()->GetTypeParam()));
|
|
}
|
|
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
|
|
int GetVersionResilientMethodHashCode(MethodDesc *pMD)
|
|
{
|
|
STANDARD_VM_CONTRACT;
|
|
|
|
int hashCode = GetVersionResilientTypeHashCode(TypeHandle(pMD->GetMethodTable()));
|
|
|
|
// Todo: Add signature to hash.
|
|
if (pMD->GetNumGenericMethodArgs() > 0)
|
|
{
|
|
hashCode ^= ComputeGenericInstanceHashCode(ComputeNameHashCode(pMD->GetName()), pMD->GetNumGenericMethodArgs(), pMD->GetMethodInstantiation(), GetVersionResilientTypeHashCode);
|
|
}
|
|
else
|
|
{
|
|
hashCode ^= ComputeNameHashCode(pMD->GetName());
|
|
}
|
|
|
|
return hashCode;
|
|
}
|
|
|
|
int GetVersionResilientModuleHashCode(Module* pModule)
|
|
{
|
|
return ComputeNameHashCode(pModule->GetSimpleName());
|
|
}
|
|
|
|
class ILInstructionParser
|
|
{
|
|
const uint8_t *_pCode;
|
|
uint32_t _cbCode;
|
|
|
|
public:
|
|
ILInstructionParser(const uint8_t *pCode, uint32_t cbCode) :
|
|
_pCode(pCode), _cbCode(cbCode)
|
|
{}
|
|
|
|
bool GetByte(uint8_t *data)
|
|
{
|
|
if (_cbCode >= 1)
|
|
{
|
|
*data = *_pCode;
|
|
_cbCode--;
|
|
_pCode++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GetUInt16(uint16_t *data)
|
|
{
|
|
if (_cbCode >= 2)
|
|
{
|
|
*data = *(uint16_t UNALIGNED*)_pCode;
|
|
_cbCode -= 2;
|
|
_pCode += 2;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GetUInt32(uint32_t *data)
|
|
{
|
|
if (_cbCode >= 4)
|
|
{
|
|
*data = *(uint32_t UNALIGNED*)_pCode;
|
|
_cbCode -= 4;
|
|
_pCode += 4;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsEmpty()
|
|
{
|
|
return _cbCode == 0;
|
|
}
|
|
};
|
|
|
|
// Use the SigParser type to handle bounds checks safely
|
|
bool AddVersionResilientHashCodeForInstruction(ILInstructionParser *parser, xxHash *hash)
|
|
{
|
|
uint16_t opcodeValue;
|
|
BYTE firstByte;
|
|
if (!parser->GetByte(&firstByte))
|
|
{
|
|
return false;
|
|
}
|
|
if (firstByte != 0xFE)
|
|
{
|
|
opcodeValue = 0xFF00 | firstByte;
|
|
}
|
|
else
|
|
{
|
|
BYTE secondByte;
|
|
if (!parser->GetByte(&secondByte))
|
|
{
|
|
return false;
|
|
}
|
|
opcodeValue = (((uint16_t)firstByte) << 8) | (uint16_t)secondByte;
|
|
}
|
|
|
|
hash->Add(opcodeValue);
|
|
|
|
opcode_format_t opcodeFormat;
|
|
switch (opcodeValue)
|
|
{
|
|
#define OPDEF_REAL_OPCODES_ONLY
|
|
#define OPDEF(name, stringname, stackpop, stackpush, params, kind, len, byte1, byte2, ctrl) \
|
|
case (((uint16_t)byte1) << 8) | (uint16_t)byte2: opcodeFormat = params; break;
|
|
#include "opcode.def"
|
|
#undef OPDEF
|
|
#undef OPDEF_REAL_OPCODES_ONLY
|
|
default: _ASSERTE(false); return false;
|
|
}
|
|
|
|
switch (opcodeFormat)
|
|
{
|
|
case InlineNone: // no inline args
|
|
break;
|
|
|
|
case ShortInlineI:
|
|
case ShortInlineBrTarget:
|
|
case ShortInlineVar: // 1 byte value which is token change resilient
|
|
{
|
|
uint8_t varValue;
|
|
if (!parser->GetByte(&varValue))
|
|
return false;
|
|
hash->Add(varValue);
|
|
break;
|
|
}
|
|
|
|
case InlineVar: // 2 byte value which is token change resilient
|
|
{
|
|
uint16_t varValue;
|
|
if (!parser->GetUInt16(&varValue))
|
|
return false;
|
|
hash->Add(varValue);
|
|
break;
|
|
}
|
|
case InlineI:
|
|
case InlineBrTarget:
|
|
case ShortInlineR: // 4 byte value which is token change resilient
|
|
{
|
|
uint32_t varValue;
|
|
if (!parser->GetUInt32(&varValue))
|
|
return false;
|
|
hash->Add(varValue);
|
|
break;
|
|
}
|
|
|
|
case InlineR:
|
|
case InlineI8: // 8 byte value which is token change resilient
|
|
{
|
|
// Handle as a pair of 4 byte values
|
|
uint32_t varValue;
|
|
uint32_t varValue2;
|
|
if (!parser->GetUInt32(&varValue))
|
|
return false;
|
|
if (!parser->GetUInt32(&varValue2))
|
|
return false;
|
|
hash->Add(varValue);
|
|
hash->Add(varValue2);
|
|
break;
|
|
}
|
|
|
|
case InlineSwitch:
|
|
{
|
|
// Switch is variable length, so use a variable length hash function
|
|
uint32_t switchCount;
|
|
if (!parser->GetUInt32(&switchCount))
|
|
return false;
|
|
|
|
hash->Add(opcodeValue);
|
|
hash->Add(switchCount);
|
|
for (;switchCount > 0; switchCount--)
|
|
{
|
|
uint32_t switchEntry;
|
|
if (!parser->GetUInt32(&switchEntry))
|
|
return false;
|
|
hash->Add(switchEntry);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case InlineMethod:
|
|
case InlineField:
|
|
case InlineType:
|
|
case InlineString:
|
|
case InlineSig:
|
|
case InlineTok:
|
|
{
|
|
// 4 byte value which is token dependent. Ignore.
|
|
uint32_t varValue;
|
|
if (!parser->GetUInt32(&varValue))
|
|
return false;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Bad code
|
|
_ASSERTE(FALSE);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetVersionResilientILCodeHashCode(MethodDesc *pMD, int* hashCode, unsigned* ilSize)
|
|
{
|
|
uint32_t maxStack;
|
|
uint32_t EHCount;
|
|
const BYTE* pILCode;
|
|
uint32_t cbILCode;
|
|
bool initLocals;
|
|
SigParser localSig;
|
|
|
|
xxHash hashILData;
|
|
|
|
if (pMD->IsDynamicMethod())
|
|
{
|
|
DynamicResolver * pResolver = pMD->AsDynamicMethodDesc()->GetResolver();
|
|
CorInfoOptions options;
|
|
pILCode = pResolver->GetCodeInfo(&cbILCode,
|
|
&maxStack,
|
|
&options,
|
|
&EHCount);
|
|
|
|
localSig = pResolver->GetLocalSig();
|
|
|
|
initLocals = (options & CORINFO_OPT_INIT_LOCALS) == CORINFO_OPT_INIT_LOCALS;
|
|
}
|
|
else if (!pMD->HasILHeader())
|
|
{
|
|
// Dynamically generated IL methods like UnsafeAccessors may not have
|
|
// an IL header.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
COR_ILMETHOD_DECODER header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL);
|
|
|
|
pILCode = header.Code;
|
|
cbILCode = header.GetCodeSize();
|
|
maxStack = header.GetMaxStack();
|
|
EHCount = header.EHCount();
|
|
initLocals = (header.GetFlags() & CorILMethod_InitLocals) == CorILMethod_InitLocals;
|
|
localSig = SigParser(header.LocalVarSig, header.cbLocalVarSig);
|
|
|
|
for (uint32_t ehClause = 0; ehClause < EHCount; ehClause++)
|
|
{
|
|
IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehClauseBuf;
|
|
auto ehInfo = header.EH->EHClause(ehClause, &ehClauseBuf);
|
|
|
|
hashILData.Add(ehInfo->Flags);
|
|
hashILData.Add(ehInfo->TryOffset);
|
|
hashILData.Add(ehInfo->TryLength);
|
|
hashILData.Add(ehInfo->HandlerLength);
|
|
hashILData.Add(ehInfo->HandlerOffset);
|
|
if ((ehInfo->Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == COR_ILEXCEPTION_CLAUSE_FILTER)
|
|
{
|
|
hashILData.Add(ehInfo->FilterOffset);
|
|
}
|
|
// Do not hash the classToken field, as is possibly token dependent
|
|
}
|
|
}
|
|
|
|
hashILData.Add(maxStack);
|
|
hashILData.Add(EHCount);
|
|
|
|
ILInstructionParser ilParser(pILCode, cbILCode);
|
|
*ilSize = cbILCode;
|
|
while (!ilParser.IsEmpty())
|
|
{
|
|
if (!AddVersionResilientHashCodeForInstruction(&ilParser, &hashILData))
|
|
return false;
|
|
}
|
|
|
|
// TODO! Analyze if adding hash of non-token depenendent portions of local signature is useful
|
|
*hashCode = (int)hashILData.ToHashCode();
|
|
return true;
|
|
}
|
|
|
|
|
|
#endif // DACCESS_COMPILE
|