diff --git a/src/dnmd/CMakeLists.txt b/src/dnmd/CMakeLists.txt index 410da6f4..5d7a5ecc 100644 --- a/src/dnmd/CMakeLists.txt +++ b/src/dnmd/CMakeLists.txt @@ -7,7 +7,6 @@ set(SOURCES query.c streams.c tables.c - sig.c write.c ) diff --git a/src/dnmd/sig.c b/src/dnmd/sig.c deleted file mode 100644 index 10de266f..00000000 --- a/src/dnmd/sig.c +++ /dev/null @@ -1,274 +0,0 @@ -#include "internal.h" - -bool md_is_field_sig(uint8_t const* sig, size_t sig_len) -{ - if (sig_len == 0) - return false; - - assert(sig != NULL); - return (*sig & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD; -} - -static bool skip_if_sentinel(uint8_t const** sig, size_t* sig_length) -{ - assert(sig != NULL && sig_length != NULL && *sig_length != 0); - - if ((*sig)[0] != ELEMENT_TYPE_SENTINEL) - return false; - - advance_stream(sig, sig_length, 1); - return true; -} - -// Given a signature buffer, skips over a parameter or return type as defined by the following sections of the ECMA spec: -// II.23.2.10 Param -// II.23.2.11 RetType -// II.23.2.12 Type -static bool skip_sig_element(uint8_t const** sig, size_t* sig_length) -{ - assert(sig != NULL && sig_length != NULL && *sig_length != 0); - - uint8_t elem_type; - uint32_t ignored_compressed_u32_arg; - if (!read_u8(sig, sig_length, &elem_type)) - { - return false; - } - - assert(elem_type != ELEMENT_TYPE_SENTINEL && "The SENTINEL element should be handled by the caller."); - - switch (elem_type) - { - case ELEMENT_TYPE_VOID: - case ELEMENT_TYPE_BOOLEAN: - case ELEMENT_TYPE_CHAR: - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - 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_STRING: - case ELEMENT_TYPE_OBJECT: - case ELEMENT_TYPE_TYPEDBYREF: - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - return true; - case ELEMENT_TYPE_FNPTR: - { - // We need to read a whole MethodDefSig (II.23.2.1) or MethodRefSig (II.23.2.2) here - // See II.23.2.12 Type for more details - uint8_t call_conv; - if (!read_u8(sig, sig_length, &call_conv)) - return false; - - uint32_t generic_arg_count = 0; - if ((call_conv & IMAGE_CEE_CS_CALLCONV_GENERIC) == IMAGE_CEE_CS_CALLCONV_GENERIC) - { - if (!decompress_u32(sig, sig_length, &generic_arg_count)) - return false; - } - - uint32_t param_count; - if (!decompress_u32(sig, sig_length, ¶m_count)) - return false; - - // skip return type - if (!skip_sig_element(sig, sig_length)) - return false; - - // skip parameters - for (uint32_t i = 0; i < param_count; i++) - { - // If we see the SENTINEL element, we'll skip it. - // As defined in II.23.2.2, the ParamCount field counts the number of - // Param instances, and SENTINEL is a separate entity in the signature - // than the Param instances. - (void)skip_if_sentinel(sig, sig_length); - if (!skip_sig_element(sig, sig_length)) - return false; - } - return true; - } - case ELEMENT_TYPE_PTR: - case ELEMENT_TYPE_BYREF: - case ELEMENT_TYPE_SZARRAY: - case ELEMENT_TYPE_PINNED: - return skip_sig_element(sig, sig_length); - - case ELEMENT_TYPE_VAR: - case ELEMENT_TYPE_MVAR: - case ELEMENT_TYPE_VALUETYPE: - case ELEMENT_TYPE_CLASS: - return decompress_u32(sig, sig_length, &ignored_compressed_u32_arg); - - case ELEMENT_TYPE_CMOD_REQD: - case ELEMENT_TYPE_CMOD_OPT: - if (!decompress_u32(sig, sig_length, &ignored_compressed_u32_arg)) - return false; - return skip_sig_element(sig, sig_length); - - case ELEMENT_TYPE_ARRAY: - { - // type - if (!skip_sig_element(sig, sig_length)) - return false; - // rank - if (!decompress_u32(sig, sig_length, &ignored_compressed_u32_arg)) - return false; - - uint32_t bound_count; - if (!decompress_u32(sig, sig_length, &bound_count)) - return false; - - for (uint32_t i = 0; i < bound_count; i++) - { - // bound - if (!decompress_u32(sig, sig_length, &ignored_compressed_u32_arg)) - return false; - } - - uint32_t lbound_count; - if (!decompress_u32(sig, sig_length, &lbound_count)) - return false; - - for (uint32_t i = 0; i < lbound_count; i++) - { - int32_t ignored_compressed_i32_arg; - // lbound - if (!decompress_i32(sig, sig_length, &ignored_compressed_i32_arg)) - return false; - } - return true; - } - case ELEMENT_TYPE_GENERICINST: - { - // class or value type - if (!advance_stream(sig, sig_length, 1)) - return false; - - // token - if (!decompress_u32(sig, sig_length, &ignored_compressed_u32_arg)) - return false; - - uint32_t num_generic_args; - if (!decompress_u32(sig, sig_length, &num_generic_args)) - return false; - - for (uint32_t i = 0; i < num_generic_args; ++i) - { - if (!skip_sig_element(sig, sig_length)) - return false; - } - return true; - } - } - assert(false && "Unknown element type"); - return false; -} - -bool md_create_methoddefsig_from_methodrefsig(uint8_t const* ref_sig, size_t ref_sig_len, uint8_t** def_sig, size_t* def_sig_len) -{ - if (ref_sig_len == 0 || def_sig == NULL || def_sig_len == NULL) - return false; - - assert(ref_sig != NULL); - - uint8_t const* curr = ref_sig; - size_t curr_len = ref_sig_len; - - // Consume the calling convention - uint8_t call_conv; - if (!read_u8(&curr, &curr_len, &call_conv)) - return false; - - // The MethodDefSig is the same as the MethodRefSig if the calling convention is not vararg. - // Only in the vararg case does the MethodRefSig have additional data to describe the exact vararg - // parameter list. - if ((call_conv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG) - { - *def_sig_len = ref_sig_len; - *def_sig = (uint8_t*)malloc(*def_sig_len); - if (*def_sig == NULL) - return false; - memcpy(*def_sig, ref_sig, *def_sig_len); - return true; - } - - // Consume the generic parameter count - uint32_t generic_param_count = 0; - if (call_conv & IMAGE_CEE_CS_CALLCONV_GENERIC) - { - if (!decompress_u32(&curr, &curr_len, &generic_param_count)) - return false; - } - - // Consume the parameter count - uint32_t param_count; - if (!decompress_u32(&curr, &curr_len, ¶m_count)) - return false; - - uint8_t const* return_and_parameter_start = curr; - // Skip return type - if (!skip_sig_element(&curr, &curr_len)) - return false; - - // Skip parameter types until we see the sentinel - uint32_t i = 0; - uint8_t const* def_sig_end = curr; - for (; i < param_count; i++, def_sig_end = curr) - { - if (skip_if_sentinel(&curr, &curr_len)) - break; - - if (!skip_sig_element(&curr, &curr_len)) - return false; - } - - // Now that we know the number of parameters, we can copy the MethodDefSig portion of the signature - // and update the parameter count. - // We need to account for the fact that the parameter count may be encoded with less bytes - // as it is emitted using the compressed unsigned integer format. - // A compressed integer is 32 bits in ECMA-335. - uint8_t encoded_original_param_count[sizeof(uint32_t)]; - size_t encoded_original_param_count_length = ARRAY_SIZE(encoded_original_param_count); - (void)compress_u32(param_count, encoded_original_param_count, &encoded_original_param_count_length); - - uint8_t encoded_def_param_count[sizeof(uint32_t)]; - size_t encoded_def_param_count_length = ARRAY_SIZE(encoded_def_param_count); - if (!compress_u32(i, encoded_def_param_count, &encoded_def_param_count_length)) - return false; - - size_t def_sig_buffer_len = (size_t)(def_sig_end - ref_sig) - encoded_original_param_count_length + encoded_def_param_count_length; - uint8_t* def_sig_buffer = (uint8_t*)malloc(def_sig_buffer_len); - if (!def_sig_buffer) - return false; - - // Copy over the signature into the new buffer - { - size_t len = def_sig_buffer_len; - uint8_t* buffer = def_sig_buffer; - buffer[0] = call_conv; - (void)advance_stream((uint8_t const**)&buffer, &len, 1); - - if (call_conv & IMAGE_CEE_CS_CALLCONV_GENERIC) - { - size_t used_len; - (void)compress_u32(generic_param_count, buffer, &used_len); - (void)advance_stream((uint8_t const**)&buffer, &len, used_len); - } - memcpy(buffer, encoded_def_param_count, encoded_def_param_count_length); - (void)advance_stream((uint8_t const**)&buffer, &len, encoded_def_param_count_length); - - // Now that we've re-written the parameter count, we can copy the rest of the signature directly from the MethodRefSig - memcpy(buffer, return_and_parameter_start, len); - } - - *def_sig_len = def_sig_buffer_len; - *def_sig = def_sig_buffer; - return true; -} diff --git a/src/inc/dnmd.h b/src/inc/dnmd.h index 88813d13..ee593c45 100644 --- a/src/inc/dnmd.h +++ b/src/inc/dnmd.h @@ -476,13 +476,6 @@ md_range_result_t md_find_range_from_cursor(mdcursor_t begin, col_index_t idx, u bool md_find_token_of_range_element(mdcursor_t element, mdToken* tk); bool md_find_cursor_of_range_element(mdcursor_t element, mdcursor_t* cursor); -bool md_is_field_sig(uint8_t const* sig, size_t sig_len); - -// Create the equivalent MethodDefSig (II.23.2.1) from a MethodRefSig (II.23.2.2). -// ref_sig is a pointer to a MethodRefSig blob. -// If the return value is true, def_sig will be a pointer to malloc-d memory containing the MethodDefSig for the MethodRefSig. -bool md_create_methoddefsig_from_methodrefsig(uint8_t const* ref_sig, size_t ref_sig_len, uint8_t** def_sig, size_t* def_sig_len); - // Given a cursor, resolve any indirections to the final cursor or return the original cursor if it does not point to an indirection table. // Returns true if the cursor was not an indirect cursor or if the indirection was resolved, or false if the cursor pointed to an invalid indirection table entry. bool md_resolve_indirect_cursor(mdcursor_t c, mdcursor_t* target); diff --git a/src/inc/internal/dnmd_tools_platform.hpp b/src/inc/internal/dnmd_tools_platform.hpp index 6b52064f..efb69f18 100644 --- a/src/inc/internal/dnmd_tools_platform.hpp +++ b/src/inc/internal/dnmd_tools_platform.hpp @@ -7,104 +7,7 @@ #include #include "dnmd_platform.hpp" - -template -class span -{ -protected: - T* _ptr; - size_t _size; -public: - span() - : _ptr{} - , _size{} - { } - - span(T* ptr, size_t len) - : _ptr{ ptr }, _size{ len } - { } - - span(span const & other) = default; - - span& operator=(span&& other) noexcept = default; - - size_t size() const noexcept - { - return _size; - } - - operator T* () noexcept - { - return _ptr; - } - - operator T const* () const noexcept - { - return _ptr; - } - - T& operator[](size_t idx) - { - if (_ptr == nullptr) - throw std::runtime_error{ "Deref null" }; - if (idx >= _size) - throw std::out_of_range{ "Out of bounds access" }; - return _ptr[idx]; - } -}; - -template -class owning_span final : public span -{ -public: - owning_span() : span{} - { } - - owning_span(T* ptr, size_t len) - : span{ ptr, len } - { } - - owning_span(owning_span&& other) noexcept - : span{} - { - *this = std::move(other); - } - - ~owning_span() - { - Deleter{}(this->_ptr); - } - - owning_span& operator=(owning_span&& other) noexcept - { - if (this->_ptr != nullptr) - Deleter{}(this->_ptr); - - this->_ptr = other._ptr; - this->_size = other._size; - other._ptr = {}; - other._size = {}; - return *this; - } - - T* release() noexcept - { - T* tmp = this->_ptr; - this->_ptr = {}; - return tmp; - } -}; - -struct free_deleter final -{ - void operator()(void* ptr) - { - free(ptr); - } -}; - -template -using malloc_span = owning_span; +#include "span.hpp" inline bool create_mdhandle(malloc_span const& buffer, mdhandle_ptr& handle) { diff --git a/src/inc/internal/span.hpp b/src/inc/internal/span.hpp new file mode 100644 index 00000000..464c9f42 --- /dev/null +++ b/src/inc/internal/span.hpp @@ -0,0 +1,113 @@ +#ifndef _SRC_INC_INTERNAL_SPAN_HPP_ +#define _SRC_INC_INTERNAL_SPAN_HPP_ + +#include +#include + +template +class span +{ +protected: + T* _ptr; + size_t _size; +public: + span() + : _ptr{} + , _size{} + { } + + span(T* ptr, size_t len) + : _ptr{ ptr }, _size{ len } + { } + + span(span const & other) = default; + + span& operator=(span&& other) noexcept = default; + + size_t size() const noexcept + { + return _size; + } + + operator T* () noexcept + { + return _ptr; + } + + operator T const* () const noexcept + { + return _ptr; + } + + T& operator[](size_t idx) + { + if (_ptr == nullptr) + throw std::runtime_error{ "Deref null" }; + if (idx >= _size) + throw std::out_of_range{ "Out of bounds access" }; + return _ptr[idx]; + } +}; + +template +class owning_span final : public span +{ +public: + owning_span() : span{} + { } + + owning_span(T* ptr, size_t len) + : span{ ptr, len } + { } + + owning_span(owning_span&& other) noexcept + : span{} + { + *this = std::move(other); + } + + ~owning_span() + { + Deleter{}(this->_ptr); + } + + owning_span& operator=(owning_span&& other) noexcept + { + if (this->_ptr != nullptr) + Deleter{}(this->_ptr); + + this->_ptr = other._ptr; + this->_size = other._size; + other._ptr = {}; + other._size = {}; + return *this; + } + + T* release() noexcept + { + T* tmp = this->_ptr; + this->_ptr = {}; + return tmp; + } +}; + +struct free_deleter final +{ + void operator()(void* ptr) + { + std::free(ptr); + } +}; + +template +using malloc_span = owning_span; + +template +span slice(span b, size_t offset) +{ + if (offset > b.size()) + throw std::out_of_range{ "Out of bounds access" }; + return { b + offset, b.size() - offset }; +} + +#endif // _SRC_INC_INTERNAL_SPAN_HPP_ \ No newline at end of file diff --git a/src/interfaces/CMakeLists.txt b/src/interfaces/CMakeLists.txt index db3e666a..d120ec75 100644 --- a/src/interfaces/CMakeLists.txt +++ b/src/interfaces/CMakeLists.txt @@ -3,16 +3,19 @@ set(SOURCES metadataimport.cpp hcorenum.cpp pal.cpp + signatures.cpp ) set(HEADERS ../inc/dnmd_interfaces.hpp + ../inc/internal/span.hpp ./metadataimportro.hpp ./hcorenum.hpp ./controllingiunknown.hpp ./tearoffbase.hpp ./pal.hpp ./dnmdowner.hpp + ./signatures.hpp ) if(NOT MSVC) diff --git a/src/interfaces/metadataimport.cpp b/src/interfaces/metadataimport.cpp index 584a9558..0c60f1ea 100644 --- a/src/interfaces/metadataimport.cpp +++ b/src/interfaces/metadataimport.cpp @@ -3,6 +3,7 @@ #include "pal.hpp" #include "metadataimportro.hpp" #include "hcorenum.hpp" +#include "signatures.hpp" #include #define MD_MODULE_TOKEN TokenFromRid(1, mdtModule) @@ -1112,12 +1113,15 @@ HRESULT STDMETHODCALLTYPE MetadataImportRO::FindMethod( if (!md_get_column_value_as_range(typedefCursor, mdtTypeDef_MethodList, &methodCursor, &count)) return CLDB_E_FILE_CORRUPT; - uint8_t* defSig; - size_t defSigLen; - if (!md_create_methoddefsig_from_methodrefsig(pvSigBlob, cbSigBlob, &defSig, &defSigLen)) + malloc_span methodDefSig; + try + { + methodDefSig = GetMethodDefSigFromMethodRefSig({ (uint8_t*)pvSigBlob, (size_t)cbSigBlob }); + } + catch (std::exception const&) + { return E_INVALIDARG; - - malloc_ptr methodDefSig{ defSig }; + } pal::StringConvert cvt{ szName }; if (!cvt.Success()) @@ -1149,8 +1153,8 @@ HRESULT STDMETHODCALLTYPE MetadataImportRO::FindMethod( uint32_t sigLen; if (1 != md_get_column_value_as_blob(target, mdtMethodDef_Signature, 1, &sig, &sigLen)) return CLDB_E_FILE_CORRUPT; - if (sigLen != defSigLen - || ::memcmp(methodDefSig.get(), sig, sigLen) != 0) + if (sigLen != methodDefSig.size() + || ::memcmp(methodDefSig, sig, sigLen) != 0) { continue; } diff --git a/src/interfaces/signatures.cpp b/src/interfaces/signatures.cpp new file mode 100644 index 00000000..db392184 --- /dev/null +++ b/src/interfaces/signatures.cpp @@ -0,0 +1,280 @@ +#include "signatures.hpp" +#include +#include +#include + +namespace +{ + std::tuple> read_compressed_uint(span signature) + { + ULONG value; + signature = slice(signature, CorSigUncompressData(signature, &value)); + return std::make_tuple(value, signature); + } + + std::tuple> read_compressed_int(span signature) + { + int value; + signature = slice(signature, CorSigUncompressSignedInt(signature, &value)); + return std::make_tuple(value, signature); + } + + std::tuple> read_compressed_token(span signature) + { + mdToken value; + signature = slice(signature, CorSigUncompressToken(signature, &value)); + return std::make_tuple(value, signature); + } + + + struct signature_element_part_tag + { + }; + + struct raw_byte_tag : signature_element_part_tag + { + }; + + struct compressed_uint_tag : signature_element_part_tag + { + }; + + struct compressed_int_tag : signature_element_part_tag + { + }; + + struct token_tag : signature_element_part_tag + { + }; + + template + span WalkSignatureElement(span signature, TCallback callback) + { + uint8_t elementType = signature[0]; + signature = slice(signature, 1); + + callback(elementType, raw_byte_tag{}); + switch (elementType) + { + case ELEMENT_TYPE_VOID: + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + 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_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_SENTINEL: + break; + case ELEMENT_TYPE_FNPTR: + { + uint8_t callingConvention = signature[0]; + signature = slice(signature, 1); + callback(callingConvention, raw_byte_tag{}); + + uint32_t genericParameterCount; + if ((callingConvention & IMAGE_CEE_CS_CALLCONV_GENERIC) == IMAGE_CEE_CS_CALLCONV_GENERIC) + { + std::tie(genericParameterCount, signature) = read_compressed_uint(signature); + callback(genericParameterCount, compressed_uint_tag{}); + } + + uint32_t parameterCount; + std::tie(parameterCount, signature) = read_compressed_uint(signature); + callback(parameterCount, compressed_uint_tag{}); + + // Walk the return type + signature = WalkSignatureElement(signature, callback); + + // Walk the parameters + for (uint32_t i = 0; i < parameterCount; i++) + { + if (signature[0] == ELEMENT_TYPE_SENTINEL) + { + signature = slice(signature, 1); + callback(ELEMENT_TYPE_SENTINEL, raw_byte_tag{}); + } + + signature = WalkSignatureElement(signature, callback); + } + break; + } + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_PINNED: + signature = WalkSignatureElement(signature, callback); + break; + + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + { + uint32_t genericParameterIndex; + std::tie(genericParameterIndex, signature) = read_compressed_uint(signature); + callback(genericParameterIndex, compressed_uint_tag{}); + break; + } + + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + mdToken token; + std::tie(token, signature) = read_compressed_token(signature); + callback(token, token_tag{}); + break; + } + + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + { + mdToken token; + std::tie(token, signature) = read_compressed_token(signature); + callback(token, token_tag{}); + signature = WalkSignatureElement(signature, callback); + break; + } + + case ELEMENT_TYPE_ARRAY: + { + signature = WalkSignatureElement(signature, callback); + + uint32_t rank; + std::tie(rank, signature) = read_compressed_uint(signature); + callback(rank, compressed_uint_tag{}); + + uint32_t numSizes; + std::tie(numSizes, signature) = read_compressed_uint(signature); + callback(numSizes, compressed_uint_tag{}); + + for (uint32_t i = 0; i < numSizes; i++) + { + uint32_t size; + std::tie(size, signature) = read_compressed_uint(signature); + callback(size, compressed_uint_tag{}); + } + + uint32_t numLoBounds; + std::tie(numLoBounds, signature) = read_compressed_uint(signature); + callback(numLoBounds, compressed_uint_tag{}); + + for (uint32_t i = 0; i < numLoBounds; i++) + { + int32_t loBound; + std::tie(loBound, signature) = read_compressed_int(signature); + callback(loBound, compressed_int_tag{}); + } + break; + } + + case ELEMENT_TYPE_GENERICINST: + { + signature = WalkSignatureElement(signature, callback); + + uint32_t genericArgumentCount; + std::tie(genericArgumentCount, signature) = read_compressed_uint(signature); + callback(genericArgumentCount, compressed_uint_tag{}); + + for (uint32_t i = 0; i < genericArgumentCount; i++) + { + signature = WalkSignatureElement(signature, callback); + } + break; + } + default: + throw std::invalid_argument { "Invalid signature element type" }; + } + + return signature; + } +} + +malloc_span GetMethodDefSigFromMethodRefSig(span methodRefSig) +{ + assert(methodRefSig.size() > 0); + // We don't need to do anything with the various elements of the signature, + // we just need to know how many parameters are before the sentinel. + span signature = methodRefSig; + uint8_t const callingConvention = signature[0]; + signature = slice(signature, 1); + + // The MethodDefSig is the same as the MethodRefSig if the calling convention is not vararg. + // Only in the vararg case does the MethodRefSig have additional data to describe the exact vararg + // parameter list. + if ((callingConvention & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG) + { + malloc_span methodDefSig{ (uint8_t*)std::malloc(methodRefSig.size()), methodRefSig.size() }; + std::memcpy(methodDefSig, methodRefSig, methodRefSig.size()); + return methodDefSig; + } + + uint32_t genericParameterCount = 0; + if ((callingConvention & IMAGE_CEE_CS_CALLCONV_GENERIC) == IMAGE_CEE_CS_CALLCONV_GENERIC) + { + std::tie(genericParameterCount, signature) = read_compressed_uint(signature); + } + + uint32_t originalParameterCount; + std::tie(originalParameterCount, signature) = read_compressed_uint(signature); + + // Save this part of the signature for us to copy later. + span returnTypeAndParameters = signature; + // Walk the return type + // Use std::intmax_t here as it can handle all the values of the various parts of the signature. + // If we were using C++14, we could use auto here instead. + signature = WalkSignatureElement(signature, [](std::intmax_t, signature_element_part_tag) { }); + + // Walk the parameters + uint32_t i = 0; + for (; i < originalParameterCount; i++) + { + if (signature[0] == ELEMENT_TYPE_SENTINEL) + { + break; + } + + signature = WalkSignatureElement(signature, [](std::intmax_t, signature_element_part_tag) { }); + } + + // Now that we know the number of parameters, we can copy the MethodDefSig portion of the signature + // and update the parameter count. + // We need to account for the fact that the parameter count may be encoded with less bytes + // as it is emitted using the compressed unsigned integer format. + // An ECMA-335 compressed integer will take up no more than 4 bytes. + uint8_t buffer[4]; + ULONG originalParamCountCompressedSize = CorSigCompressData(originalParameterCount, buffer); + ULONG newParamCountCompressedSize = CorSigCompressData(i, buffer); + span compressedNewParamCount = { buffer, newParamCountCompressedSize }; + + // The MethodDefSig length will be the length of the original signature up to the ELEMENT_TYPE_SENTINEL value, + // minus the difference in the compressed size of the original parameter count and the new parameter count, if any. + size_t methodDefSigBufferLength = methodRefSig.size() - signature.size() - originalParamCountCompressedSize + newParamCountCompressedSize; + malloc_span methodDefSigBuffer{ (uint8_t*)std::malloc(methodDefSigBufferLength), methodDefSigBufferLength }; + + // Copy over the signature into the new buffer. + // In case the parameter count was encoded with less bytes, we need to account for that + // and copy the signature piece by piece. + size_t offset = 0; + methodDefSigBuffer[offset++] = callingConvention; + if ((callingConvention & IMAGE_CEE_CS_CALLCONV_GENERIC) == IMAGE_CEE_CS_CALLCONV_GENERIC) + { + offset += CorSigCompressData(genericParameterCount, methodDefSigBuffer + offset); + } + std::memcpy(methodDefSigBuffer + offset, compressedNewParamCount, newParamCountCompressedSize); + offset += newParamCountCompressedSize; + + // Now that we've re-written the parameter count, we can copy the rest of the signature directly from the MethodRefSig + assert(returnTypeAndParameters.size() >= methodDefSigBufferLength - offset); + std::memcpy(methodDefSigBuffer + offset, returnTypeAndParameters, methodDefSigBufferLength - offset); + + return methodDefSigBuffer; +} diff --git a/src/interfaces/signatures.hpp b/src/interfaces/signatures.hpp new file mode 100644 index 00000000..65713591 --- /dev/null +++ b/src/interfaces/signatures.hpp @@ -0,0 +1,13 @@ +#ifndef _SRC_INTERFACES_SIGNATURES_HPP_ +#define _SRC_INTERFACES_SIGNATURES_HPP_ + +#include +#include + +#include + +#include + +malloc_span GetMethodDefSigFromMethodRefSig(span methodRefSig); + +#endif // _SRC_INTERFACES_SIGNATURES_HPP_