1620 lines
51 KiB
C++
1620 lines
51 KiB
C++
#include "clr_helpers.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include <set>
|
|
#include <stack>
|
|
#include "dd_profiler_constants.h"
|
|
#include "environment_variables.h"
|
|
#include "logging.h"
|
|
#include "macros.h"
|
|
#include "pal.h"
|
|
#include "sig_helpers.h"
|
|
|
|
namespace trace {
|
|
|
|
RuntimeInformation GetRuntimeInformation(ICorProfilerInfo4* info) {
|
|
COR_PRF_RUNTIME_TYPE runtime_type;
|
|
USHORT major_version;
|
|
USHORT minor_version;
|
|
USHORT build_version;
|
|
USHORT qfe_version;
|
|
|
|
auto hr = info->GetRuntimeInformation(nullptr, &runtime_type, &major_version, &minor_version, &build_version, &qfe_version, 0, nullptr, nullptr);
|
|
if (FAILED(hr)) {
|
|
return {};
|
|
}
|
|
|
|
return {runtime_type, major_version, minor_version, build_version, qfe_version};
|
|
}
|
|
|
|
AssemblyInfo GetAssemblyInfo(ICorProfilerInfo4* info,
|
|
const AssemblyID& assembly_id) {
|
|
WCHAR assembly_name[kNameMaxSize];
|
|
DWORD assembly_name_len = 0;
|
|
AppDomainID app_domain_id;
|
|
ModuleID manifest_module_id;
|
|
|
|
auto hr = info->GetAssemblyInfo(assembly_id, kNameMaxSize, &assembly_name_len,
|
|
assembly_name, &app_domain_id, &manifest_module_id);
|
|
|
|
if (FAILED(hr) || assembly_name_len == 0) {
|
|
return {};
|
|
}
|
|
|
|
WCHAR app_domain_name[kNameMaxSize];
|
|
DWORD app_domain_name_len = 0;
|
|
|
|
hr = info->GetAppDomainInfo(app_domain_id, kNameMaxSize, &app_domain_name_len,
|
|
app_domain_name, nullptr);
|
|
|
|
if (FAILED(hr) || app_domain_name_len == 0) {
|
|
return {};
|
|
}
|
|
|
|
return {assembly_id, WSTRING(assembly_name), manifest_module_id, app_domain_id,
|
|
WSTRING(app_domain_name)};
|
|
}
|
|
|
|
AssemblyMetadata GetAssemblyImportMetadata(
|
|
const ComPtr<IMetaDataAssemblyImport>& assembly_import) {
|
|
mdAssembly current = mdAssemblyNil;
|
|
auto hr = assembly_import->GetAssemblyFromScope(¤t);
|
|
if (FAILED(hr)) {
|
|
return {};
|
|
}
|
|
WCHAR name[kNameMaxSize];
|
|
DWORD name_len = 0;
|
|
ASSEMBLYMETADATA assembly_metadata{};
|
|
DWORD assembly_flags = 0;
|
|
const ModuleID placeholder_module_id = 0;
|
|
|
|
hr = assembly_import->GetAssemblyProps(current, nullptr, nullptr, nullptr,
|
|
name, kNameMaxSize, &name_len,
|
|
&assembly_metadata, &assembly_flags);
|
|
if (FAILED(hr) || name_len == 0) {
|
|
return {};
|
|
}
|
|
return AssemblyMetadata(
|
|
placeholder_module_id, name, current, assembly_metadata.usMajorVersion,
|
|
assembly_metadata.usMinorVersion, assembly_metadata.usBuildNumber,
|
|
assembly_metadata.usRevisionNumber);
|
|
}
|
|
|
|
AssemblyMetadata GetReferencedAssemblyMetadata(
|
|
const ComPtr<IMetaDataAssemblyImport>& assembly_import,
|
|
const mdAssemblyRef& assembly_ref) {
|
|
WCHAR name[kNameMaxSize];
|
|
DWORD name_len = 0;
|
|
ASSEMBLYMETADATA assembly_metadata{};
|
|
DWORD assembly_flags = 0;
|
|
const ModuleID module_id_placeholder = 0;
|
|
const auto hr = assembly_import->GetAssemblyRefProps(
|
|
assembly_ref, nullptr, nullptr, name, kNameMaxSize, &name_len,
|
|
&assembly_metadata, nullptr, nullptr, &assembly_flags);
|
|
if (FAILED(hr) || name_len == 0) {
|
|
return {};
|
|
}
|
|
return AssemblyMetadata(
|
|
module_id_placeholder, name, assembly_ref,
|
|
assembly_metadata.usMajorVersion, assembly_metadata.usMinorVersion,
|
|
assembly_metadata.usBuildNumber, assembly_metadata.usRevisionNumber);
|
|
}
|
|
|
|
std::vector<BYTE> GetSignatureByteRepresentation(
|
|
ULONG signature_length, PCCOR_SIGNATURE raw_signature) {
|
|
std::vector<BYTE> signature_data(signature_length);
|
|
for (ULONG i = 0; i < signature_length; i++) {
|
|
signature_data[i] = raw_signature[i];
|
|
}
|
|
|
|
return signature_data;
|
|
}
|
|
|
|
FunctionInfo GetFunctionInfo(const ComPtr<IMetaDataImport2>& metadata_import, const mdToken& token) {
|
|
mdToken parent_token = mdTokenNil;
|
|
mdToken method_spec_token = mdTokenNil;
|
|
mdToken method_def_token = mdTokenNil;
|
|
WCHAR function_name[kNameMaxSize]{};
|
|
DWORD function_name_len = 0;
|
|
|
|
PCCOR_SIGNATURE raw_signature;
|
|
ULONG raw_signature_len;
|
|
BOOL is_generic = false;
|
|
std::vector<BYTE> final_signature_bytes;
|
|
std::vector<BYTE> method_spec_signature;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
const auto token_type = TypeFromToken(token);
|
|
switch (token_type) {
|
|
case mdtMemberRef:
|
|
hr = metadata_import->GetMemberRefProps(
|
|
token, &parent_token, function_name, kNameMaxSize, &function_name_len,
|
|
&raw_signature, &raw_signature_len);
|
|
break;
|
|
case mdtMethodDef:
|
|
hr = metadata_import->GetMemberProps(
|
|
token, &parent_token, function_name, kNameMaxSize, &function_name_len,
|
|
nullptr, &raw_signature, &raw_signature_len, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr);
|
|
break;
|
|
case mdtMethodSpec: {
|
|
hr = metadata_import->GetMethodSpecProps(
|
|
token, &parent_token, &raw_signature, &raw_signature_len);
|
|
is_generic = true;
|
|
if (FAILED(hr)) {
|
|
return {};
|
|
}
|
|
const auto generic_info = GetFunctionInfo(metadata_import, parent_token);
|
|
final_signature_bytes = generic_info.signature.data;
|
|
method_spec_signature =
|
|
GetSignatureByteRepresentation(raw_signature_len, raw_signature);
|
|
std::memcpy(function_name, generic_info.name.c_str(),
|
|
sizeof(WCHAR) * (generic_info.name.length() + 1));
|
|
function_name_len = DWORD(generic_info.name.length() + 1);
|
|
method_spec_token = token;
|
|
method_def_token = generic_info.id;
|
|
} break;
|
|
default:
|
|
Warn("[trace::GetFunctionInfo] unknown token type: {}", token_type);
|
|
return {};
|
|
}
|
|
if (FAILED(hr) || function_name_len == 0) {
|
|
return {};
|
|
}
|
|
|
|
// parent_token could be: TypeDef, TypeRef, TypeSpec, ModuleRef, MethodDef
|
|
const auto type_info = GetTypeInfo(metadata_import, parent_token);
|
|
|
|
if (is_generic) {
|
|
// use the generic constructor and feed both method signatures
|
|
return {method_spec_token,
|
|
WSTRING(function_name),
|
|
type_info,
|
|
MethodSignature(final_signature_bytes),
|
|
MethodSignature(method_spec_signature),
|
|
method_def_token, FunctionMethodSignature(raw_signature, raw_signature_len)};
|
|
}
|
|
|
|
final_signature_bytes =
|
|
GetSignatureByteRepresentation(raw_signature_len, raw_signature);
|
|
|
|
return {token, WSTRING(function_name), type_info,
|
|
MethodSignature(final_signature_bytes),
|
|
FunctionMethodSignature(raw_signature, raw_signature_len)};
|
|
}
|
|
|
|
ModuleInfo GetModuleInfo(ICorProfilerInfo4* info, const ModuleID& module_id) {
|
|
const DWORD module_path_size = 260;
|
|
WCHAR module_path[module_path_size]{};
|
|
DWORD module_path_len = 0;
|
|
LPCBYTE base_load_address;
|
|
AssemblyID assembly_id = 0;
|
|
DWORD module_flags = 0;
|
|
const HRESULT hr = info->GetModuleInfo2(
|
|
module_id, &base_load_address, module_path_size, &module_path_len,
|
|
module_path, &assembly_id, &module_flags);
|
|
if (FAILED(hr) || module_path_len == 0) {
|
|
return {};
|
|
}
|
|
return {module_id, WSTRING(module_path), GetAssemblyInfo(info, assembly_id),
|
|
module_flags};
|
|
}
|
|
|
|
TypeInfo GetTypeInfo(const ComPtr<IMetaDataImport2>& metadata_import,
|
|
const mdToken& token) {
|
|
mdToken parent_token = mdTokenNil;
|
|
TypeInfo* parentTypeInfo = nullptr;
|
|
mdToken parent_type_token = mdTokenNil;
|
|
WCHAR type_name[kNameMaxSize]{};
|
|
DWORD type_name_len = 0;
|
|
DWORD type_flags;
|
|
TypeInfo* extendsInfo = nullptr;
|
|
mdToken type_extends = mdTokenNil;
|
|
bool type_valueType = false;
|
|
bool type_isGeneric = false;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
const auto token_type = TypeFromToken(token);
|
|
|
|
switch (token_type) {
|
|
case mdtTypeDef:
|
|
hr = metadata_import->GetTypeDefProps(token, type_name, kNameMaxSize,
|
|
&type_name_len, &type_flags,
|
|
&type_extends);
|
|
|
|
metadata_import->GetNestedClassProps(token, &parent_type_token);
|
|
if (parent_type_token != mdTokenNil) {
|
|
parentTypeInfo = new TypeInfo(GetTypeInfo(metadata_import, parent_type_token));
|
|
}
|
|
|
|
if (type_extends != mdTokenNil) {
|
|
extendsInfo = new TypeInfo(GetTypeInfo(metadata_import, type_extends));
|
|
type_valueType = extendsInfo->name == WStr("System.ValueType") ||
|
|
extendsInfo->name == WStr("System.Enum");
|
|
}
|
|
break;
|
|
case mdtTypeRef:
|
|
hr = metadata_import->GetTypeRefProps(token, &parent_token, type_name,
|
|
kNameMaxSize, &type_name_len);
|
|
break;
|
|
case mdtTypeSpec: {
|
|
PCCOR_SIGNATURE signature{};
|
|
ULONG signature_length{};
|
|
|
|
hr = metadata_import->GetTypeSpecFromToken(token, &signature,
|
|
&signature_length);
|
|
|
|
if (FAILED(hr) || signature_length < 3) {
|
|
return {};
|
|
}
|
|
|
|
if (signature[0] & ELEMENT_TYPE_GENERICINST) {
|
|
mdToken type_token;
|
|
CorSigUncompressToken(&signature[2], &type_token);
|
|
const auto baseType = GetTypeInfo(metadata_import, type_token);
|
|
return {baseType.id, baseType.name, token, token_type,
|
|
baseType.extend_from,
|
|
baseType.valueType,
|
|
baseType.isGeneric,
|
|
baseType.parent_type};
|
|
}
|
|
} break;
|
|
case mdtModuleRef:
|
|
metadata_import->GetModuleRefProps(token, type_name, kNameMaxSize, &type_name_len);
|
|
break;
|
|
case mdtMemberRef:
|
|
return GetFunctionInfo(metadata_import, token).type;
|
|
break;
|
|
case mdtMethodDef:
|
|
return GetFunctionInfo(metadata_import, token).type;
|
|
break;
|
|
}
|
|
if (FAILED(hr) || type_name_len == 0) {
|
|
return {};
|
|
}
|
|
|
|
const auto type_name_string = WSTRING(type_name);
|
|
const auto generic_token_index = type_name_string.rfind(WStr("`"));
|
|
if (generic_token_index != std::string::npos) {
|
|
const auto idxFromRight = type_name_string.length() - generic_token_index - 1;
|
|
type_isGeneric = idxFromRight == 1 || idxFromRight == 2;
|
|
}
|
|
|
|
return { token, type_name_string, mdTypeSpecNil, token_type, extendsInfo, type_valueType, type_isGeneric, parentTypeInfo };
|
|
}
|
|
|
|
mdAssemblyRef FindAssemblyRef(
|
|
const ComPtr<IMetaDataAssemblyImport>& assembly_import,
|
|
const WSTRING& assembly_name) {
|
|
for (mdAssemblyRef assembly_ref : EnumAssemblyRefs(assembly_import)) {
|
|
if (GetReferencedAssemblyMetadata(assembly_import, assembly_ref).name ==
|
|
assembly_name) {
|
|
return assembly_ref;
|
|
}
|
|
}
|
|
return mdAssemblyRefNil;
|
|
}
|
|
|
|
std::vector<Integration> FilterIntegrationsByName(
|
|
const std::vector<Integration>& integrations,
|
|
const std::vector<WSTRING>& disabled_integration_names) {
|
|
std::vector<Integration> enabled;
|
|
|
|
for (auto& i : integrations) {
|
|
bool disabled = false;
|
|
for (auto& disabled_integration : disabled_integration_names) {
|
|
if (i.integration_name == disabled_integration) {
|
|
// this integration is disabled, skip it
|
|
disabled = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!disabled) {
|
|
enabled.push_back(i);
|
|
}
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
std::vector<IntegrationMethod> FlattenIntegrations(
|
|
const std::vector<Integration>& integrations,
|
|
bool is_calltarget_enabled) {
|
|
std::vector<IntegrationMethod> flattened;
|
|
|
|
for (auto& i : integrations) {
|
|
for (auto& mr : i.method_replacements) {
|
|
const auto isCallTargetIntegration =
|
|
mr.wrapper_method.action == calltarget_modification_action;
|
|
|
|
if (is_calltarget_enabled && isCallTargetIntegration) {
|
|
flattened.emplace_back(i.integration_name, mr);
|
|
} else if (!is_calltarget_enabled && !isCallTargetIntegration) {
|
|
flattened.emplace_back(i.integration_name, mr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return flattened;
|
|
}
|
|
|
|
std::vector<IntegrationMethod> FilterIntegrationsByCaller(
|
|
const std::vector<IntegrationMethod>& integration_methods,
|
|
const AssemblyInfo assembly) {
|
|
std::vector<IntegrationMethod> enabled;
|
|
|
|
for (auto& i : integration_methods) {
|
|
if (i.replacement.caller_method.assembly.name.empty() ||
|
|
i.replacement.caller_method.assembly.name == assembly.name) {
|
|
enabled.push_back(i);
|
|
}
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
bool AssemblyMeetsIntegrationRequirements(
|
|
const AssemblyMetadata metadata,
|
|
const MethodReplacement method_replacement) {
|
|
const auto target = method_replacement.target_method;
|
|
|
|
if (target.assembly.name != metadata.name) {
|
|
// not the expected assembly
|
|
return false;
|
|
}
|
|
|
|
if (target.min_version > metadata.version) {
|
|
return false;
|
|
}
|
|
|
|
if (target.max_version < metadata.version) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<IntegrationMethod> FilterIntegrationsByTarget(
|
|
const std::vector<IntegrationMethod>& integration_methods,
|
|
const ComPtr<IMetaDataAssemblyImport>& assembly_import) {
|
|
std::vector<IntegrationMethod> enabled;
|
|
|
|
const auto assembly_metadata = GetAssemblyImportMetadata(assembly_import);
|
|
|
|
for (auto& i : integration_methods) {
|
|
bool found = false;
|
|
if (AssemblyMeetsIntegrationRequirements(assembly_metadata, i.replacement)) {
|
|
found = true;
|
|
} else {
|
|
for (auto& assembly_ref : EnumAssemblyRefs(assembly_import)) {
|
|
const auto metadata_ref = GetReferencedAssemblyMetadata(assembly_import, assembly_ref);
|
|
if (AssemblyMeetsIntegrationRequirements(metadata_ref, i.replacement)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
enabled.push_back(i);
|
|
}
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
std::vector<IntegrationMethod> FilterIntegrationsByTargetAssemblyName(
|
|
const std::vector<IntegrationMethod>& integration_methods,
|
|
const std::vector<WSTRING>& excluded_assembly_names) {
|
|
std::vector<IntegrationMethod> methods;
|
|
|
|
for (auto& i : integration_methods) {
|
|
bool assembly_excluded = false;
|
|
|
|
for (auto& excluded_assembly_name : excluded_assembly_names) {
|
|
if (i.replacement.target_method.assembly.name == excluded_assembly_name) {
|
|
assembly_excluded = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!assembly_excluded) {
|
|
methods.emplace_back(i);
|
|
}
|
|
}
|
|
|
|
return methods;
|
|
}
|
|
|
|
mdMethodSpec DefineMethodSpec(const ComPtr<IMetaDataEmit2>& metadata_emit,
|
|
const mdToken& token,
|
|
const MethodSignature& signature) {
|
|
mdMethodSpec spec = mdMethodSpecNil;
|
|
auto hr = metadata_emit->DefineMethodSpec(
|
|
token, signature.data.data(), ULONG(signature.data.size()), &spec);
|
|
if (FAILED(hr)) {
|
|
Warn("[DefineMethodSpec] failed to define method spec");
|
|
}
|
|
return spec;
|
|
}
|
|
|
|
TypeInfo RetrieveTypeForSignature(
|
|
const ComPtr<IMetaDataImport2>& metadata_import,
|
|
const FunctionInfo& function_info, const size_t current_index,
|
|
ULONG& token_length) {
|
|
mdToken type_token;
|
|
const auto type_token_start =
|
|
PCCOR_SIGNATURE(&function_info.signature.data[current_index]);
|
|
token_length = CorSigUncompressToken(type_token_start, &type_token);
|
|
auto type_data = GetTypeInfo(metadata_import, type_token);
|
|
return type_data;
|
|
}
|
|
|
|
bool TryParseSignatureTypes(const ComPtr<IMetaDataImport2>& metadata_import,
|
|
const FunctionInfo& function_info,
|
|
std::vector<WSTRING>& signature_result) {
|
|
try {
|
|
const auto signature_size = function_info.signature.data.size();
|
|
const auto generic_count = function_info.signature.NumberOfTypeArguments();
|
|
const auto param_count = function_info.signature.NumberOfArguments();
|
|
size_t current_index = 2; // Where the parameters actually start
|
|
|
|
if (generic_count > 0) {
|
|
current_index++; // offset by one because the method is generic
|
|
}
|
|
|
|
const auto expected_number_of_types = param_count + 1;
|
|
size_t current_type_index = 0;
|
|
std::vector<WSTRING> type_names(expected_number_of_types);
|
|
|
|
std::stack<int> generic_arg_stack;
|
|
WSTRING append_to_type = WStr("");
|
|
WSTRING current_type_name = WStr("");
|
|
|
|
for (; current_index < signature_size; current_index++) {
|
|
mdToken type_token;
|
|
ULONG token_length;
|
|
auto param_piece = function_info.signature.data[current_index];
|
|
const auto cor_element_type = CorElementType(param_piece);
|
|
|
|
switch (cor_element_type) {
|
|
case ELEMENT_TYPE_VOID: {
|
|
current_type_name.append(WStr("System.Void"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_BOOLEAN: {
|
|
current_type_name.append(WStr("System.Boolean"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_CHAR: {
|
|
current_type_name.append(WStr("System.Char16"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_I1: {
|
|
current_type_name.append(WStr("System.SByte"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_U1: {
|
|
current_type_name.append(WStr("System.Byte"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_I2: {
|
|
current_type_name.append(WStr("System.Int16"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_U2: {
|
|
current_type_name.append(WStr("System.UInt16"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_I4: {
|
|
current_type_name.append(WStr("System.Int32"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_U4: {
|
|
current_type_name.append(WStr("System.UInt32"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_I8: {
|
|
current_type_name.append(WStr("System.Int64"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_U8: {
|
|
current_type_name.append(WStr("System.UInt64"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_R4: {
|
|
current_type_name.append(WStr("System.Single"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_R8: {
|
|
current_type_name.append(WStr("System.Double"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_STRING: {
|
|
current_type_name.append(WStr("System.String"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_OBJECT: {
|
|
current_type_name.append(WStr("System.Object"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_VALUETYPE:
|
|
case ELEMENT_TYPE_CLASS: {
|
|
current_index++;
|
|
auto type_data = RetrieveTypeForSignature(
|
|
metadata_import, function_info, current_index, token_length);
|
|
|
|
mdToken examined_type_token = type_data.id;
|
|
auto examined_type_name = type_data.name;
|
|
auto ongoing_type_name = examined_type_name;
|
|
|
|
// check for whether this may be a nested class
|
|
while (examined_type_name.find_first_of(WStr(".")) == std::string::npos) {
|
|
// This may possibly be a nested class, check for the parent
|
|
mdToken potentialParentToken;
|
|
metadata_import->GetNestedClassProps(examined_type_token,
|
|
&potentialParentToken);
|
|
|
|
if (potentialParentToken == mdTokenNil) {
|
|
break;
|
|
}
|
|
|
|
auto nesting_type =
|
|
GetTypeInfo(metadata_import, potentialParentToken);
|
|
|
|
examined_type_token = nesting_type.id;
|
|
examined_type_name = nesting_type.name;
|
|
|
|
ongoing_type_name = examined_type_name + WStr("+") + ongoing_type_name;
|
|
}
|
|
|
|
// index will be moved up one on every loop
|
|
// handle tokens which have more than one byte
|
|
current_index += token_length - 1;
|
|
current_type_name.append(ongoing_type_name);
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_SZARRAY: {
|
|
append_to_type.append(WStr("[]"));
|
|
while (function_info.signature.data[(current_index + 1)] ==
|
|
ELEMENT_TYPE_SZARRAY) {
|
|
append_to_type.append(WStr("[]"));
|
|
current_index++;
|
|
}
|
|
// Next will be the type of the array(s)
|
|
continue;
|
|
}
|
|
|
|
case ELEMENT_TYPE_MVAR: {
|
|
// We are likely parsing a standalone generic param
|
|
token_length = CorSigUncompressToken(
|
|
PCCOR_SIGNATURE(&function_info.signature.data[current_index]),
|
|
&type_token);
|
|
current_type_name.append(WStr("T"));
|
|
current_index += token_length;
|
|
// TODO: implement conventions for generics (eg., TC1, TC2, TM1, TM2)
|
|
// current_type_name.append(std::to_wstring(type_token));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_VAR: {
|
|
// We are likely within a generic variant
|
|
token_length = CorSigUncompressToken(
|
|
PCCOR_SIGNATURE(&function_info.signature.data[current_index]),
|
|
&type_token);
|
|
current_type_name.append(WStr("T"));
|
|
current_index += token_length;
|
|
// TODO: implement conventions for generics (eg., TC1, TC2, TM1, TM2)
|
|
// current_type_name.append(std::to_wstring(type_token));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_GENERICINST: {
|
|
// skip past generic type indicator token
|
|
current_index++;
|
|
// skip past actual generic type token (probably a class)
|
|
current_index++;
|
|
const auto generic_type_data = RetrieveTypeForSignature(
|
|
metadata_import, function_info, current_index, token_length);
|
|
auto type_name = generic_type_data.name;
|
|
current_type_name.append(type_name);
|
|
current_type_name.append(WStr("<")); // Begin generic args
|
|
|
|
// Because we are starting a new generic, decrement any existing level
|
|
if (!generic_arg_stack.empty()) {
|
|
generic_arg_stack.top()--;
|
|
}
|
|
|
|
// figure out how many generic args this type has
|
|
const auto index_of_tick = type_name.find_last_of('`');
|
|
auto num_args_text = ToString(type_name.substr(index_of_tick + 1));
|
|
auto actual_arg_count = std::stoi(num_args_text, nullptr);
|
|
generic_arg_stack.push(actual_arg_count);
|
|
current_index += token_length;
|
|
// Next will be the variants
|
|
continue;
|
|
}
|
|
|
|
case ELEMENT_TYPE_BYREF: {
|
|
// TODO: This hasn't been encountered yet
|
|
current_type_name.append(WStr("ref"));
|
|
break;
|
|
}
|
|
|
|
case ELEMENT_TYPE_END: {
|
|
// we already handle the generic by counting args
|
|
continue;
|
|
}
|
|
|
|
default: {
|
|
// This is unexpected and we should report that, and not instrument
|
|
current_type_name.append(ToWSTRING(ToString(cor_element_type)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!append_to_type.empty()) {
|
|
current_type_name.append(append_to_type);
|
|
append_to_type = WStr("");
|
|
}
|
|
|
|
if (!generic_arg_stack.empty()) {
|
|
// decrement this level's args
|
|
generic_arg_stack.top()--;
|
|
|
|
if (generic_arg_stack.top() > 0) {
|
|
// we're in the middle of generic type args
|
|
current_type_name.append(WStr(", "));
|
|
}
|
|
}
|
|
|
|
while (!generic_arg_stack.empty() && generic_arg_stack.top() == 0) {
|
|
// unwind the generics with no args left
|
|
generic_arg_stack.pop();
|
|
current_type_name.append(WStr(">"));
|
|
|
|
if (!generic_arg_stack.empty() && generic_arg_stack.top() > 0) {
|
|
// We are in a nested generic and we need a comma to separate args
|
|
current_type_name.append(WStr(", "));
|
|
}
|
|
}
|
|
|
|
if (!generic_arg_stack.empty()) {
|
|
continue;
|
|
}
|
|
|
|
if (current_type_index >= expected_number_of_types) {
|
|
// We missed something, drop out for safety
|
|
return false;
|
|
}
|
|
|
|
type_names[current_type_index] = current_type_name;
|
|
current_type_name = WStr("");
|
|
current_type_index++;
|
|
}
|
|
|
|
signature_result = type_names;
|
|
|
|
} catch (...) {
|
|
// TODO: Add precise exceptions and log
|
|
// We were unable to parse for some reason
|
|
// Return that we've failed
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
HRESULT CreateAssemblyRefToMscorlib(const ComPtr<IMetaDataAssemblyEmit>& assembly_emit, mdAssemblyRef* mscorlib_ref) {
|
|
// Define an AssemblyRef to mscorlib, needed to create TypeRefs later
|
|
ASSEMBLYMETADATA metadata{};
|
|
metadata.usMajorVersion = 4;
|
|
metadata.usMinorVersion = 0;
|
|
metadata.usBuildNumber = 0;
|
|
metadata.usRevisionNumber = 0;
|
|
BYTE public_key[] = {0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89};
|
|
HRESULT hr = assembly_emit->DefineAssemblyRef(public_key, sizeof(public_key),
|
|
WStr("mscorlib"), &metadata, NULL, 0, 0,
|
|
mscorlib_ref);
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool ReturnTypeTokenforValueTypeElementType(PCCOR_SIGNATURE p_sig,
|
|
const ComPtr<IMetaDataEmit2>& metadata_emit,
|
|
const ComPtr<IMetaDataAssemblyEmit>& assembly_emit,
|
|
mdToken* ret_type_token) {
|
|
const auto cor_element_type = CorElementType(*p_sig);
|
|
WSTRING managed_type_name = WStr("");
|
|
|
|
switch (cor_element_type) {
|
|
case ELEMENT_TYPE_VALUETYPE: {
|
|
ULONG result;
|
|
result = CorSigUncompressToken(p_sig + 1, ret_type_token);
|
|
if (result == -1) {
|
|
Warn("[trace::ReturnTypeTokenforElementType] ELEMENT_TYPE_VALUETYPE failed to find uncompress TypeRef or TypeDef");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case ELEMENT_TYPE_VOID: // 0x01 // System.Void (struct)
|
|
managed_type_name = WStr("System.Void");
|
|
break;
|
|
case ELEMENT_TYPE_BOOLEAN: // 0x02 // System.Boolean (struct)
|
|
managed_type_name = WStr("System.Boolean");
|
|
break;
|
|
case ELEMENT_TYPE_CHAR: // 0x03 // System.Char (struct)
|
|
managed_type_name = WStr("System.Char");
|
|
break;
|
|
case ELEMENT_TYPE_I1: // 0x04 // System.SByte (struct)
|
|
managed_type_name = WStr("System.SByte");
|
|
break;
|
|
case ELEMENT_TYPE_U1: // 0x05 // System.Byte (struct)
|
|
managed_type_name = WStr("System.Byte");
|
|
break;
|
|
case ELEMENT_TYPE_I2: // 0x06 // System.Int16 (struct)
|
|
managed_type_name = WStr("System.Int16");
|
|
break;
|
|
case ELEMENT_TYPE_U2: // 0x07 // System.UInt16 (struct)
|
|
managed_type_name = WStr("System.UInt16");
|
|
break;
|
|
case ELEMENT_TYPE_I4: // 0x08 // System.Int32 (struct)
|
|
managed_type_name = WStr("System.Int32");
|
|
break;
|
|
case ELEMENT_TYPE_U4: // 0x09 // System.UInt32 (struct)
|
|
managed_type_name = WStr("System.UInt32");
|
|
break;
|
|
case ELEMENT_TYPE_I8: // 0x0a // System.Int64 (struct)
|
|
managed_type_name = WStr("System.Int64");
|
|
break;
|
|
case ELEMENT_TYPE_U8: // 0x0b // System.UInt64 (struct)
|
|
managed_type_name = WStr("System.UInt64");
|
|
break;
|
|
case ELEMENT_TYPE_R4: // 0x0c // System.Single (struct)
|
|
managed_type_name = WStr("System.Single");
|
|
break;
|
|
case ELEMENT_TYPE_R8: // 0x0d // System.Double (struct)
|
|
managed_type_name = WStr("System.Double");
|
|
break;
|
|
case ELEMENT_TYPE_TYPEDBYREF: // 0X16 // System.TypedReference (struct)
|
|
managed_type_name = WStr("System.TypedReference");
|
|
break;
|
|
case ELEMENT_TYPE_I: // 0x18 // System.IntPtr (struct)
|
|
managed_type_name = WStr("System.IntPtr");
|
|
break;
|
|
case ELEMENT_TYPE_U: // 0x19 // System.UIntPtr (struct)
|
|
managed_type_name = WStr("System.UIntPtr");
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Create reference to Mscorlib
|
|
mdModuleRef mscorlib_ref;
|
|
HRESULT hr;
|
|
hr = CreateAssemblyRefToMscorlib(assembly_emit, &mscorlib_ref);
|
|
|
|
if (FAILED(hr)) {
|
|
Warn("[trace::ReturnTypeTokenforElementType] failed to define AssemblyRef to mscorlib");
|
|
return false;
|
|
}
|
|
|
|
// Create/Get TypeRef to the listed type
|
|
if (managed_type_name == WStr("")) {
|
|
Warn("[trace::ReturnTypeTokenforElementType] no managed type name given");
|
|
return false;
|
|
}
|
|
|
|
hr = metadata_emit->DefineTypeRefByName(
|
|
mscorlib_ref, managed_type_name.c_str(), ret_type_token);
|
|
|
|
if (FAILED(hr)) {
|
|
Warn("[trace::ReturnTypeTokenforElementType] unable to create type ref for managed_type_name=", managed_type_name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ReturnTypeIsValueTypeOrGeneric(
|
|
const ComPtr<IMetaDataImport2>& metadata_import,
|
|
const ComPtr<IMetaDataEmit2>& metadata_emit,
|
|
const ComPtr<IMetaDataAssemblyEmit>& assembly_emit,
|
|
const mdToken targetFunctionToken,
|
|
const MethodSignature targetFunctionSignature,
|
|
mdToken* ret_type_token) {
|
|
|
|
// MethodDefSig Format: [[HASTHIS] [EXPLICITTHIS]] (DEFAULT|VARARG|GENERIC GenParamCount) ParamCount RetType Param* [SENTINEL Param+]
|
|
const auto generic_count = targetFunctionSignature.NumberOfTypeArguments();
|
|
size_t method_def_sig_index = generic_count == 0 ? 2 : 3; // Initialize the index to point to RetType
|
|
auto ret_type_byte = targetFunctionSignature.data[method_def_sig_index];
|
|
const auto ret_type = CorElementType(ret_type_byte);
|
|
|
|
switch (ret_type) {
|
|
case ELEMENT_TYPE_VOID:
|
|
// No object is returned, so return false.
|
|
return false;
|
|
|
|
case ELEMENT_TYPE_GENERICINST: {
|
|
// Format: GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *
|
|
// Example: Task<HttpResponseMessage>. Return true if the type is a VALUETYPE
|
|
if (targetFunctionSignature.data[method_def_sig_index + 1] != ELEMENT_TYPE_VALUETYPE) {
|
|
return false;
|
|
}
|
|
|
|
PCCOR_SIGNATURE p_start_byte = PCCOR_SIGNATURE(&targetFunctionSignature.data[method_def_sig_index]);
|
|
PCCOR_SIGNATURE p_end_byte = p_start_byte;
|
|
if (!ParseType(&p_end_byte)) {
|
|
return false;
|
|
}
|
|
|
|
size_t length = p_end_byte - p_start_byte;
|
|
HRESULT hr = metadata_emit->GetTokenFromTypeSpec(p_start_byte, (ULONG) length, ret_type_token);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
case ELEMENT_TYPE_VAR:
|
|
case ELEMENT_TYPE_MVAR: {
|
|
// Format: VAR number
|
|
// Format: MVAR number
|
|
|
|
// Extract the number, which is an index into the generic type arguments of the method or the type
|
|
method_def_sig_index++; // Advance the current_index to point to "number"
|
|
ULONG generic_type_index;
|
|
if (CorSigUncompressData(PCCOR_SIGNATURE(&targetFunctionSignature.data[method_def_sig_index]),
|
|
&generic_type_index) == -1) {
|
|
Warn("[trace::ReturnTypeIsValueTypeOrGeneric] element_type=", ret_type, ": unable to read VAR|MVAR index");
|
|
return false;
|
|
}
|
|
|
|
// Get the signature of the MethodSpec or the method's parent TypeSpec
|
|
// Each spec will clearly list the types used for the generic type variables
|
|
const auto token_type = TypeFromToken(targetFunctionToken);
|
|
mdToken parent_token = mdTokenNil;
|
|
HRESULT hr;
|
|
PCCOR_SIGNATURE spec_signature{};
|
|
ULONG spec_signature_length{};
|
|
|
|
switch (token_type) {
|
|
case mdtMemberRef:
|
|
// The compiler will never make method calls to generic methods without
|
|
// the generic context, so we never expect to hit this at
|
|
// run-time. If we are evaluating the MethodDef/MethodRef of a generic
|
|
// method return false because it is invalid.
|
|
if (generic_count > 0) {
|
|
return false;
|
|
}
|
|
|
|
hr = metadata_import->GetMemberRefProps(targetFunctionToken,
|
|
&parent_token, nullptr, 0,
|
|
nullptr, nullptr, nullptr);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = metadata_import->GetTypeSpecFromToken(parent_token, &spec_signature,
|
|
&spec_signature_length);
|
|
}
|
|
break;
|
|
case mdtMethodDef:
|
|
// The compiler will never make method calls to generic methods without
|
|
// the generic context, so we never expect to hit this at
|
|
// run-time. If we are evaluating the MethodDef/MethodRef of a generic
|
|
// method return false because it is invalid.
|
|
if (generic_count > 0) {
|
|
return false;
|
|
}
|
|
|
|
hr = metadata_import->GetMemberProps(
|
|
targetFunctionToken, &parent_token, nullptr, 0, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = metadata_import->GetTypeSpecFromToken(parent_token, &spec_signature,
|
|
&spec_signature_length);
|
|
}
|
|
break;
|
|
case mdtMethodSpec:
|
|
hr = metadata_import->GetMethodSpecProps(targetFunctionToken,
|
|
&parent_token, &spec_signature, &spec_signature_length);
|
|
break;
|
|
default:
|
|
Warn("[trace::ReturnTypeIsValueTypeOrGeneric] element_type=", ret_type, ": function token was not a MemberRef, MethodDef, or MethodSpec");
|
|
return false;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
Warn("[trace::ReturnTypeIsValueTypeOrGeneric] element_type=", ret_type, ": failed to get parent token or signature");
|
|
return false;
|
|
}
|
|
|
|
// Determine the index of GenArgCount in the signature
|
|
size_t parent_token_index;
|
|
if (token_type == mdtMemberRef || token_type == mdtMethodDef) {
|
|
// TypeSpec Format: GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type Type*
|
|
// Skip over TypeDefOrRefEncoded by parsing the signature at index 2
|
|
parent_token_index = 2;
|
|
mdToken dummy_token;
|
|
ULONG token_length = CorSigUncompressToken(
|
|
&spec_signature[parent_token_index], &dummy_token);
|
|
parent_token_index += token_length;
|
|
} else if (token_type == mdtMethodSpec) {
|
|
// MethodSpec Format: GENRICINST GenArgCount Type Type*
|
|
parent_token_index = 1;
|
|
} else {
|
|
Warn("[trace::ReturnTypeIsValueTypeOrGeneric] element_type=", ret_type, ": token_type (", token_type , ") not recognized");
|
|
return false;
|
|
}
|
|
|
|
// Read the value of GenArgCount in the signature
|
|
ULONG num_generic_arguments;
|
|
parent_token_index += CorSigUncompressData(
|
|
&spec_signature[parent_token_index], &num_generic_arguments);
|
|
|
|
// Get a pointer to first type after GenArgCount that we can increment to read the signature
|
|
PCCOR_SIGNATURE p_current_byte = spec_signature + parent_token_index;
|
|
|
|
// Iterate to specified generic type argument index and return the appropriate class token or TypeSpec
|
|
for (size_t i = 0; i < num_generic_arguments; i++) {
|
|
if (i != generic_type_index) {
|
|
if (!ParseType(&p_current_byte)) {
|
|
Warn(
|
|
"[trace::ReturnTypeIsValueTypeOrGeneric] element_type=", ret_type, ": Unable to parse "
|
|
"generic type argument ", i,
|
|
"from signature of parent_token:", parent_token);
|
|
return false;
|
|
}
|
|
} else if (*p_current_byte == ELEMENT_TYPE_MVAR ||
|
|
*p_current_byte == ELEMENT_TYPE_VAR) {
|
|
// The method was defined with a method-level generic type argument from the caller. Return the TypeSpec token for the `M#` MVAR description, or
|
|
// The method was defined with a type-level generic type argument from the caller. Return the TypeSpec token for the `T#` VAR description
|
|
hr = metadata_emit->GetTokenFromTypeSpec(p_current_byte, 2,
|
|
ret_type_token);
|
|
return SUCCEEDED(hr);
|
|
} else if (*p_current_byte == ELEMENT_TYPE_GENERICINST) {
|
|
// Format: GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *
|
|
// Example: Task<HttpResponseMessage>. Return true if the type is a VALUETYPE
|
|
if (*(p_current_byte + 1) != ELEMENT_TYPE_VALUETYPE) {
|
|
return false;
|
|
}
|
|
|
|
PCCOR_SIGNATURE p_start_byte = p_current_byte;
|
|
PCCOR_SIGNATURE p_end_byte = p_start_byte;
|
|
if (!ParseType(&p_end_byte)) {
|
|
return false;
|
|
}
|
|
|
|
size_t length = p_end_byte - p_start_byte;
|
|
HRESULT hr = metadata_emit->GetTokenFromTypeSpec(p_start_byte, (ULONG) length,
|
|
ret_type_token);
|
|
return SUCCEEDED(hr);
|
|
} else {
|
|
return ReturnTypeTokenforValueTypeElementType(
|
|
p_current_byte,
|
|
metadata_emit,
|
|
assembly_emit,
|
|
ret_type_token);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
return ReturnTypeTokenforValueTypeElementType(
|
|
PCCOR_SIGNATURE(&targetFunctionSignature.data[method_def_sig_index]),
|
|
metadata_emit,
|
|
assembly_emit,
|
|
ret_type_token);
|
|
}
|
|
}
|
|
|
|
|
|
// FunctionMethodArgument
|
|
int FunctionMethodArgument::GetTypeFlags(unsigned& elementType) const {
|
|
int flag = 0;
|
|
PCCOR_SIGNATURE pbCur = &pbBase[offset];
|
|
|
|
if (*pbCur == ELEMENT_TYPE_VOID) {
|
|
elementType = ELEMENT_TYPE_VOID;
|
|
flag |= TypeFlagVoid;
|
|
return flag;
|
|
}
|
|
|
|
if (*pbCur == ELEMENT_TYPE_BYREF) {
|
|
pbCur++;
|
|
flag |= TypeFlagByRef;
|
|
}
|
|
|
|
elementType = *pbCur;
|
|
|
|
switch (*pbCur) {
|
|
case ELEMENT_TYPE_BOOLEAN:
|
|
case ELEMENT_TYPE_CHAR:
|
|
case ELEMENT_TYPE_I1:
|
|
case ELEMENT_TYPE_U1:
|
|
case ELEMENT_TYPE_U2:
|
|
case ELEMENT_TYPE_I2:
|
|
case ELEMENT_TYPE_I4:
|
|
case ELEMENT_TYPE_U4:
|
|
case ELEMENT_TYPE_I8:
|
|
case ELEMENT_TYPE_U8:
|
|
case ELEMENT_TYPE_R4:
|
|
case ELEMENT_TYPE_R8:
|
|
case ELEMENT_TYPE_I:
|
|
case ELEMENT_TYPE_U:
|
|
case ELEMENT_TYPE_VALUETYPE:
|
|
case ELEMENT_TYPE_MVAR:
|
|
case ELEMENT_TYPE_VAR:
|
|
flag |= TypeFlagBoxedType;
|
|
break;
|
|
case ELEMENT_TYPE_GENERICINST:
|
|
pbCur++;
|
|
if (*pbCur == ELEMENT_TYPE_VALUETYPE) {
|
|
flag |= TypeFlagBoxedType;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
mdToken FunctionMethodArgument::GetTypeTok(ComPtr<IMetaDataEmit2>& pEmit, mdAssemblyRef corLibRef) const {
|
|
mdToken token = mdTokenNil;
|
|
PCCOR_SIGNATURE pbCur = &pbBase[offset];
|
|
const PCCOR_SIGNATURE pStart = pbCur;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_BYREF) {
|
|
pbCur++;
|
|
}
|
|
|
|
switch (*pbCur) {
|
|
case ELEMENT_TYPE_BOOLEAN:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemBoolean, &token);
|
|
break;
|
|
case ELEMENT_TYPE_CHAR:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemChar, &token);
|
|
break;
|
|
case ELEMENT_TYPE_I1:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemSByte, &token);
|
|
break;
|
|
case ELEMENT_TYPE_U1:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemByte, &token);
|
|
break;
|
|
case ELEMENT_TYPE_U2:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemUInt16, &token);
|
|
break;
|
|
case ELEMENT_TYPE_I2:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemInt16, &token);
|
|
break;
|
|
case ELEMENT_TYPE_I4:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemInt32, &token);
|
|
break;
|
|
case ELEMENT_TYPE_U4:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemUInt32, &token);
|
|
break;
|
|
case ELEMENT_TYPE_I8:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemInt64, &token);
|
|
break;
|
|
case ELEMENT_TYPE_U8:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemUInt64, &token);
|
|
break;
|
|
case ELEMENT_TYPE_R4:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemSingle, &token);
|
|
break;
|
|
case ELEMENT_TYPE_R8:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemDouble, &token);
|
|
break;
|
|
case ELEMENT_TYPE_I:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemIntPtr, &token);
|
|
break;
|
|
case ELEMENT_TYPE_U:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemUIntPtr, &token);
|
|
break;
|
|
case ELEMENT_TYPE_STRING:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemString, &token);
|
|
break;
|
|
case ELEMENT_TYPE_OBJECT:
|
|
pEmit->DefineTypeRefByName(corLibRef, SystemObject, &token);
|
|
break;
|
|
case ELEMENT_TYPE_CLASS:
|
|
pbCur++;
|
|
token = CorSigUncompressToken(pbCur);
|
|
break;
|
|
case ELEMENT_TYPE_VALUETYPE:
|
|
pbCur++;
|
|
token = CorSigUncompressToken(pbCur);
|
|
break;
|
|
case ELEMENT_TYPE_GENERICINST:
|
|
case ELEMENT_TYPE_SZARRAY:
|
|
case ELEMENT_TYPE_MVAR:
|
|
case ELEMENT_TYPE_VAR:
|
|
pEmit->GetTokenFromTypeSpec(
|
|
pbCur, length - static_cast<ULONG>(pbCur - pStart), &token);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
WSTRING GetSigTypeTokName(PCCOR_SIGNATURE& pbCur, const ComPtr<IMetaDataImport2>& pImport) {
|
|
WSTRING tokenName = WStr("");
|
|
bool ref_flag = false;
|
|
if (*pbCur == ELEMENT_TYPE_BYREF) {
|
|
pbCur++;
|
|
ref_flag = true;
|
|
}
|
|
|
|
switch (*pbCur) {
|
|
case ELEMENT_TYPE_BOOLEAN:
|
|
tokenName = SystemBoolean;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_CHAR:
|
|
tokenName = SystemChar;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_I1:
|
|
tokenName = SystemSByte;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_U1:
|
|
tokenName = SystemByte;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_U2:
|
|
tokenName = SystemUInt16;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_I2:
|
|
tokenName = SystemInt16;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_I4:
|
|
tokenName = SystemInt32;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_U4:
|
|
tokenName = SystemUInt32;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_I8:
|
|
tokenName = SystemInt64;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_U8:
|
|
tokenName = SystemUInt64;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_R4:
|
|
tokenName = SystemSingle;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_R8:
|
|
tokenName = SystemDouble;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_I:
|
|
tokenName = SystemIntPtr;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_U:
|
|
tokenName = SystemUIntPtr;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_STRING:
|
|
tokenName = SystemString;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_OBJECT:
|
|
tokenName = SystemObject;
|
|
pbCur++;
|
|
break;
|
|
case ELEMENT_TYPE_CLASS:
|
|
case ELEMENT_TYPE_VALUETYPE: {
|
|
pbCur++;
|
|
mdToken token;
|
|
pbCur += CorSigUncompressToken(pbCur, &token);
|
|
tokenName = GetTypeInfo(pImport, token).name;
|
|
break;
|
|
}
|
|
case ELEMENT_TYPE_SZARRAY: {
|
|
pbCur++;
|
|
tokenName = GetSigTypeTokName(pbCur, pImport) + WStr("[]");
|
|
break;
|
|
}
|
|
case ELEMENT_TYPE_GENERICINST: {
|
|
pbCur++;
|
|
tokenName = GetSigTypeTokName(pbCur, pImport);
|
|
tokenName += WStr("[");
|
|
ULONG num = 0;
|
|
pbCur += CorSigUncompressData(pbCur, &num);
|
|
for (ULONG i = 0; i < num; i++) {
|
|
tokenName += GetSigTypeTokName(pbCur, pImport);
|
|
if (i != num - 1) {
|
|
tokenName += WStr(",");
|
|
}
|
|
}
|
|
tokenName += WStr("]");
|
|
break;
|
|
}
|
|
case ELEMENT_TYPE_MVAR: {
|
|
pbCur++;
|
|
ULONG num = 0;
|
|
pbCur += CorSigUncompressData(pbCur, &num);
|
|
tokenName = WStr("!!") + ToWSTRING(std::to_string(num));
|
|
break;
|
|
}
|
|
case ELEMENT_TYPE_VAR: {
|
|
pbCur++;
|
|
ULONG num = 0;
|
|
pbCur += CorSigUncompressData(pbCur, &num);
|
|
tokenName = WStr("!") + ToWSTRING(std::to_string(num));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ref_flag) {
|
|
tokenName += WStr("&");
|
|
}
|
|
return tokenName;
|
|
}
|
|
|
|
WSTRING FunctionMethodArgument::GetTypeTokName(ComPtr<IMetaDataImport2>& pImport) const {
|
|
PCCOR_SIGNATURE pbCur = &pbBase[offset];
|
|
return GetSigTypeTokName(pbCur, pImport);
|
|
}
|
|
|
|
ULONG FunctionMethodArgument::GetSignature(PCCOR_SIGNATURE& data) const {
|
|
data = &pbBase[offset];
|
|
return length;
|
|
}
|
|
|
|
// FunctionMethodSignature
|
|
bool ParseByte(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd, unsigned char* pbOut) {
|
|
if (pbCur < pbEnd) {
|
|
*pbOut = *pbCur;
|
|
pbCur++;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ParseNumber(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd, unsigned* pOut) {
|
|
// parse the variable length number format (0-4 bytes)
|
|
|
|
unsigned char b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
|
|
|
// at least one byte in the encoding, read that
|
|
|
|
if (!ParseByte(pbCur, pbEnd, &b1)) return false;
|
|
|
|
if (b1 == 0xff) {
|
|
// special encoding of 'NULL'
|
|
// not sure what this means as a number, don't expect to see it except for
|
|
// string lengths which we don't encounter anyway so calling it an error
|
|
return false;
|
|
}
|
|
|
|
// early out on 1 byte encoding
|
|
if ((b1 & 0x80) == 0) {
|
|
*pOut = (int)b1;
|
|
return true;
|
|
}
|
|
|
|
// now at least 2 bytes in the encoding, read 2nd byte
|
|
if (!ParseByte(pbCur, pbEnd, &b2)) return false;
|
|
|
|
// early out on 2 byte encoding
|
|
if ((b1 & 0x40) == 0) {
|
|
*pOut = (((b1 & 0x3f) << 8) | b2);
|
|
return true;
|
|
}
|
|
|
|
// must be a 4 byte encoding
|
|
if ((b1 & 0x20) != 0) {
|
|
// 4 byte encoding has this bit clear -- error if not
|
|
return false;
|
|
}
|
|
|
|
if (!ParseByte(pbCur, pbEnd, &b3)) return false;
|
|
|
|
if (!ParseByte(pbCur, pbEnd, &b4)) return false;
|
|
|
|
*pOut = ((b1 & 0x1f) << 24) | (b2 << 16) | (b3 << 8) | b4;
|
|
return true;
|
|
}
|
|
|
|
bool ParseTypeDefOrRefEncoded(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd,
|
|
unsigned char* pIndexTypeOut,
|
|
unsigned* pIndexOut) {
|
|
// parse an encoded typedef or typeref
|
|
unsigned encoded = 0;
|
|
|
|
if (!ParseNumber(pbCur, pbEnd, &encoded)) return false;
|
|
|
|
*pIndexTypeOut = (unsigned char)(encoded & 0x3);
|
|
*pIndexOut = (encoded >> 2);
|
|
return true;
|
|
}
|
|
|
|
/* we don't support
|
|
PTR CustomMod* VOID
|
|
PTR CustomMod* Type
|
|
FNPTR MethodDefSig
|
|
FNPTR MethodRefSig
|
|
ARRAY Type ArrayShape
|
|
SZARRAY CustomMod+ Type (but we do support SZARRAY Type)
|
|
*/
|
|
bool ParseType(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd) {
|
|
/*
|
|
Type ::= ( BOOLEAN | CHAR | I1 | U1 | U2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 |
|
|
I | U | | VALUETYPE TypeDefOrRefEncoded | CLASS TypeDefOrRefEncoded | STRING
|
|
| OBJECT
|
|
| PTR CustomMod* VOID
|
|
| PTR CustomMod* Type
|
|
| FNPTR MethodDefSig
|
|
| FNPTR MethodRefSig
|
|
| ARRAY Type ArrayShape
|
|
| SZARRAY CustomMod* Type
|
|
| GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *
|
|
| VAR Number
|
|
| MVAR Number
|
|
*/
|
|
|
|
unsigned char elem_type;
|
|
unsigned index;
|
|
unsigned number;
|
|
unsigned char indexType;
|
|
|
|
if (!ParseByte(pbCur, pbEnd, &elem_type)) return false;
|
|
|
|
switch (elem_type) {
|
|
case ELEMENT_TYPE_BOOLEAN:
|
|
case ELEMENT_TYPE_CHAR:
|
|
case ELEMENT_TYPE_I1:
|
|
case ELEMENT_TYPE_U1:
|
|
case ELEMENT_TYPE_U2:
|
|
case ELEMENT_TYPE_I2:
|
|
case ELEMENT_TYPE_I4:
|
|
case ELEMENT_TYPE_U4:
|
|
case ELEMENT_TYPE_I8:
|
|
case ELEMENT_TYPE_U8:
|
|
case ELEMENT_TYPE_R4:
|
|
case ELEMENT_TYPE_R8:
|
|
case ELEMENT_TYPE_I:
|
|
case ELEMENT_TYPE_U:
|
|
case ELEMENT_TYPE_STRING:
|
|
case ELEMENT_TYPE_OBJECT:
|
|
// simple types
|
|
break;
|
|
|
|
case ELEMENT_TYPE_PTR:
|
|
return false;
|
|
|
|
case ELEMENT_TYPE_CLASS:
|
|
// CLASS TypeDefOrRefEncoded
|
|
if (!ParseTypeDefOrRefEncoded(pbCur, pbEnd, &indexType, &index))
|
|
return false;
|
|
break;
|
|
|
|
case ELEMENT_TYPE_VALUETYPE:
|
|
// VALUETYPE TypeDefOrRefEncoded
|
|
if (!ParseTypeDefOrRefEncoded(pbCur, pbEnd, &indexType, &index))
|
|
return false;
|
|
|
|
break;
|
|
|
|
case ELEMENT_TYPE_FNPTR:
|
|
// FNPTR MethodDefSig
|
|
// FNPTR MethodRefSig
|
|
|
|
return false;
|
|
|
|
case ELEMENT_TYPE_ARRAY:
|
|
// ARRAY Type ArrayShape
|
|
return false;
|
|
|
|
case ELEMENT_TYPE_SZARRAY:
|
|
// SZARRAY Type
|
|
|
|
if (*pbCur == ELEMENT_TYPE_CMOD_OPT || *pbCur == ELEMENT_TYPE_CMOD_REQD)
|
|
return false;
|
|
|
|
if (!ParseType(pbCur, pbEnd)) return false;
|
|
|
|
break;
|
|
|
|
case ELEMENT_TYPE_GENERICINST:
|
|
// GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *
|
|
if (!ParseByte(pbCur, pbEnd, &elem_type)) return false;
|
|
|
|
if (elem_type != ELEMENT_TYPE_CLASS &&
|
|
elem_type != ELEMENT_TYPE_VALUETYPE)
|
|
return false;
|
|
|
|
if (!ParseTypeDefOrRefEncoded(pbCur, pbEnd, &indexType, &index))
|
|
return false;
|
|
|
|
if (!ParseNumber(pbCur, pbEnd, &number)) return false;
|
|
|
|
for (unsigned i = 0; i < number; i++) {
|
|
if (!ParseType(pbCur, pbEnd)) return false;
|
|
}
|
|
break;
|
|
|
|
case ELEMENT_TYPE_VAR:
|
|
// VAR Number
|
|
if (!ParseNumber(pbCur, pbEnd, &number)) return false;
|
|
|
|
break;
|
|
|
|
case ELEMENT_TYPE_MVAR:
|
|
// MVAR Number
|
|
if (!ParseNumber(pbCur, pbEnd, &number)) return false;
|
|
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Param ::= CustomMod* ( TYPEDBYREF | [BYREF] Type )
|
|
// CustomMod* TYPEDBYREF we don't support
|
|
bool ParseParam(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd) {
|
|
if (*pbCur == ELEMENT_TYPE_CMOD_OPT || *pbCur == ELEMENT_TYPE_CMOD_REQD) {
|
|
return false;
|
|
}
|
|
|
|
if (pbCur >= pbEnd) return false;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_TYPEDBYREF) return false;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_BYREF) pbCur++;
|
|
|
|
return ParseType(pbCur, pbEnd);
|
|
}
|
|
|
|
// RetType ::= CustomMod* ( VOID | TYPEDBYREF | [BYREF] Type )
|
|
// CustomMod* TYPEDBYREF we don't support
|
|
bool ParseRetType(PCCOR_SIGNATURE& pbCur, PCCOR_SIGNATURE pbEnd) {
|
|
|
|
if (*pbCur == ELEMENT_TYPE_CMOD_OPT || *pbCur == ELEMENT_TYPE_CMOD_REQD)
|
|
return false;
|
|
|
|
if (pbCur >= pbEnd)
|
|
return false;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_TYPEDBYREF)
|
|
return false;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_VOID) {
|
|
pbCur++;
|
|
return true;
|
|
}
|
|
|
|
if (*pbCur == ELEMENT_TYPE_BYREF)
|
|
pbCur++;
|
|
|
|
return ParseType(pbCur, pbEnd);
|
|
}
|
|
|
|
|
|
HRESULT FunctionMethodSignature::TryParse() {
|
|
PCCOR_SIGNATURE pbCur = pbBase;
|
|
PCCOR_SIGNATURE pbEnd = pbBase + len;
|
|
unsigned char elem_type;
|
|
|
|
IfFalseRetFAIL(ParseByte(pbCur, pbEnd, &elem_type));
|
|
|
|
if (elem_type & IMAGE_CEE_CS_CALLCONV_GENERIC) {
|
|
unsigned gen_param_count;
|
|
IfFalseRetFAIL(ParseNumber(pbCur, pbEnd, &gen_param_count));
|
|
numberOfTypeArguments = gen_param_count;
|
|
}
|
|
|
|
unsigned param_count;
|
|
IfFalseRetFAIL(ParseNumber(pbCur, pbEnd, ¶m_count));
|
|
numberOfArguments = param_count;
|
|
|
|
const PCCOR_SIGNATURE pbRet = pbCur;
|
|
|
|
IfFalseRetFAIL(ParseRetType(pbCur, pbEnd));
|
|
ret.pbBase = pbBase;
|
|
ret.length = (ULONG)(pbCur - pbRet);
|
|
ret.offset = (ULONG)(pbCur - pbBase - ret.length);
|
|
|
|
auto fEncounteredSentinal = false;
|
|
for (unsigned i = 0; i < param_count; i++) {
|
|
if (pbCur >= pbEnd) return E_FAIL;
|
|
|
|
if (*pbCur == ELEMENT_TYPE_SENTINEL) {
|
|
if (fEncounteredSentinal) return E_FAIL;
|
|
|
|
fEncounteredSentinal = true;
|
|
pbCur++;
|
|
}
|
|
|
|
const PCCOR_SIGNATURE pbParam = pbCur;
|
|
|
|
IfFalseRetFAIL(ParseParam(pbCur, pbEnd));
|
|
|
|
FunctionMethodArgument argument{};
|
|
argument.pbBase = pbBase;
|
|
argument.length = (ULONG)(pbCur - pbParam);
|
|
argument.offset = (ULONG)(pbCur - pbBase - argument.length);
|
|
|
|
params.push_back(argument);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
bool FindTypeDefByName(
|
|
const trace::WSTRING instrumentationTargetMethodTypeName,
|
|
const trace::WSTRING assemblyName,
|
|
const ComPtr<IMetaDataImport2>& metadata_import, mdTypeDef& typeDef) {
|
|
mdTypeDef parentTypeDef = mdTypeDefNil;
|
|
auto nameParts = Split(instrumentationTargetMethodTypeName, '+');
|
|
auto instrumentedMethodTypeName = instrumentationTargetMethodTypeName;
|
|
|
|
if (nameParts.size() == 2) {
|
|
// We're instrumenting a nested class, find the parent first
|
|
auto hr = metadata_import->FindTypeDefByName(nameParts[0].c_str(),
|
|
mdTokenNil, &parentTypeDef);
|
|
|
|
if (FAILED(hr)) {
|
|
// This can happen between .NET framework and .NET core, not all apis are
|
|
// available in both. Eg: WinHttpHandler, CurlHandler, and some methods in
|
|
// System.Data
|
|
Debug("Can't load the parent TypeDef: ", nameParts[0],
|
|
" for nested class: ", instrumentationTargetMethodTypeName,
|
|
", Module: ", assemblyName);
|
|
return false;
|
|
}
|
|
instrumentedMethodTypeName = nameParts[1];
|
|
|
|
} else if (nameParts.size() > 2) {
|
|
Warn("Invalid TypeDef-only one layer of nested classes are supported: ",
|
|
instrumentationTargetMethodTypeName, ", Module: ", assemblyName);
|
|
return false;
|
|
}
|
|
|
|
// Find the type we're instrumenting
|
|
auto hr = metadata_import->FindTypeDefByName(
|
|
instrumentedMethodTypeName.c_str(), parentTypeDef, &typeDef);
|
|
if (FAILED(hr)) {
|
|
// This can happen between .NET framework and .NET core, not all apis are
|
|
// available in both. Eg: WinHttpHandler, CurlHandler, and some methods in
|
|
// System.Data
|
|
Debug("Can't load the TypeDef for: ", instrumentedMethodTypeName,
|
|
", Module: ", assemblyName);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace trace
|