mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-09 17:44:48 +09:00

* Events for IL methods without IL headers Dynamically generated methods like UnsafeAccessor functions are marked as IL, but don't contain an IL header. The lack of header is an indication the IL must be generated at runtime. Co-authored-by: Tlakaelel Axayakatl Ceja <tlakaelel.ceja@microsoft.com>
443 lines
12 KiB
C++
443 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_UNRESTOREDTYPEKEY);
|
|
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
|