diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 75b2e0d9d94400..3019faec2c1f1f 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -2931,21 +2931,14 @@ static gint32 isFixedSizeArray (MonoClassField *f) MonoClass *fixed_size_class = mono_class_try_get_fixed_buffer_class (); if (fixed_size_class != NULL && mono_class_has_parent (ctor_class, fixed_size_class)) { attr = &cinfo->attrs [aindex]; - gpointer *typed_args, *named_args; - CattrNamedArg *arginfo; - int num_named_args; - - mono_reflection_create_custom_attr_data_args_noalloc (mono_get_corlib (), attr->ctor, attr->data, attr->data_size, - &typed_args, &named_args, &num_named_args, &arginfo, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_get_corlib (), attr->ctor, attr->data, attr->data_size, error); if (!is_ok (error)) { ret = 0; goto leave; } - ret = *(gint32*)typed_args [1]; - g_free (typed_args [1]); - g_free (typed_args); - g_free (named_args); - g_free (arginfo); + + ret = *(gint32*)decoded_args->typed_args[1]->value.primitive; + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); return ret; } } diff --git a/src/mono/mono/metadata/custom-attrs-internals.h b/src/mono/mono/metadata/custom-attrs-internals.h index 16a232a80db5f4..8f0d25f563cb42 100644 --- a/src/mono/mono/metadata/custom-attrs-internals.h +++ b/src/mono/mono/metadata/custom-attrs-internals.h @@ -9,6 +9,29 @@ #include #include +typedef struct _MonoCustomAttrValueArray MonoCustomAttrValueArray; + +typedef struct _MonoCustomAttrValue { + union { + gpointer primitive; /* int/enum/MonoType/string */ + MonoCustomAttrValueArray *array; + } value; + MonoTypeEnum type : 8; +} MonoCustomAttrValue; + +struct _MonoCustomAttrValueArray { + int len; + MonoCustomAttrValue values[MONO_ZERO_LEN_ARRAY]; +}; + +typedef struct _MonoDecodeCustomAttr { + int typed_args_num; + int named_args_num; + MonoCustomAttrValue **typed_args; + MonoCustomAttrValue **named_args; + CattrNamedArg *named_args_info; +} MonoDecodeCustomAttr; + MonoCustomAttrInfo* mono_custom_attrs_from_builders (MonoImage *alloc_img, MonoImage *image, MonoArray *cattrs); @@ -33,8 +56,9 @@ MONO_COMPONENT_API void mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoArrayHandleOut typed_args_out, MonoArrayHandleOut named_args_out, CattrNamedArg **named_arg_info, MonoError *error); MONO_COMPONENT_API void -mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, - gpointer **typed_args_out, gpointer **named_args_out, int *num_named_args, - CattrNamedArg **named_arg_info, MonoError *error); +mono_reflection_free_custom_attr_data_args_noalloc(MonoDecodeCustomAttr* decoded_args); + +MONO_COMPONENT_API MonoDecodeCustomAttr* +mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoError *error); #endif /* __MONO_METADATA_REFLECTION_CUSTOM_ATTRS_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/custom-attrs.c b/src/mono/mono/metadata/custom-attrs.c index 8e61a6aa095bb7..170c6a05e43b95 100644 --- a/src/mono/mono/metadata/custom-attrs.c +++ b/src/mono/mono/metadata/custom-attrs.c @@ -377,8 +377,6 @@ load_cattr_value (MonoImage *image, MonoType *t, MonoObject **out_obj, const cha break; case MONO_TYPE_STRING: { - const char *start = p; - if (!bcheck_blob (p, 0, boundp, error)) return NULL; MONO_DISABLE_WARNING (4310) // cast truncates constant value @@ -392,8 +390,6 @@ MONO_RESTORE_WARNING if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) return NULL; *end = p + slen; - if (!out_obj) - return (void*)start; // https://bugzilla.xamarin.com/show_bug.cgi?id=60848 // Custom attribute strings are encoded as wtf-8 instead of utf-8. // If we decode using utf-8 like the spec says, we will silently fail @@ -409,14 +405,10 @@ MONO_RESTORE_WARNING } case MONO_TYPE_CLASS: { MonoType *cattr_type = load_cattr_type (image, t, TRUE, p, boundp, end, error, &slen); - if (out_obj) { - if (!cattr_type ) - return NULL; - *out_obj = (MonoObject*)mono_type_get_object_checked (cattr_type , error); + if (!cattr_type ) return NULL; - } else { - return cattr_type; - } + *out_obj = (MonoObject*)mono_type_get_object_checked (cattr_type , error); + return NULL; } case MONO_TYPE_OBJECT: { if (!bcheck_blob (p, 0, boundp, error)) @@ -428,14 +420,10 @@ MONO_RESTORE_WARNING if (subt == CATTR_TYPE_SYSTEM_TYPE) { MonoType *cattr_type = load_cattr_type (image, t, FALSE, p, boundp, end, error, &slen); - if (out_obj) { - if (!cattr_type) - return NULL; - *out_obj = (MonoObject*)mono_type_get_object_checked (cattr_type, error); + if (!cattr_type) return NULL; - } else { - return cattr_type; - } + *out_obj = (MonoObject*)mono_type_get_object_checked (cattr_type, error); + return NULL; } else if (subt == 0x0E) { type = MONO_TYPE_STRING; goto handle_enum; @@ -496,7 +484,7 @@ MONO_RESTORE_WARNING return NULL; } case MONO_TYPE_SZARRAY: { - MonoArray *arr = NULL; + MonoArray *arr; guint32 i, alen, basetype; if (!bcheck_blob (p, 3, boundp, error)) @@ -508,10 +496,8 @@ MONO_RESTORE_WARNING return NULL; } - if (out_obj) { - arr = mono_array_new_checked (tklass, alen, error); - return_val_if_nok (error, NULL); - } + arr = mono_array_new_checked (tklass, alen, error); + return_val_if_nok (error, NULL); basetype = m_class_get_byval_arg (tklass)->type; if (basetype == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tklass)) @@ -535,8 +521,7 @@ MONO_RESTORE_WARNING if (!bcheck_blob (p, 0, boundp, error)) return NULL; MonoBoolean val = *p++; - if (arr) - mono_array_set_internal (arr, MonoBoolean, i, val); + mono_array_set_internal (arr, MonoBoolean, i, val); } break; case MONO_TYPE_CHAR: @@ -546,8 +531,7 @@ MONO_RESTORE_WARNING if (!bcheck_blob (p, 1, boundp, error)) return NULL; guint16 val = read16 (p); - if (arr) - mono_array_set_internal (arr, guint16, i, val); + mono_array_set_internal (arr, guint16, i, val); p += 2; } break; @@ -558,8 +542,7 @@ MONO_RESTORE_WARNING if (!bcheck_blob (p, 3, boundp, error)) return NULL; guint32 val = read32 (p); - if (arr) - mono_array_set_internal (arr, guint32, i, val); + mono_array_set_internal (arr, guint32, i, val); p += 4; } break; @@ -569,8 +552,7 @@ MONO_RESTORE_WARNING return NULL; double val; readr8 (p, &val); - if (arr) - mono_array_set_internal (arr, double, i, val); + mono_array_set_internal (arr, double, i, val); p += 8; } break; @@ -580,8 +562,7 @@ MONO_RESTORE_WARNING if (!bcheck_blob (p, 7, boundp, error)) return NULL; guint64 val = read64 (p); - if (arr) - mono_array_set_internal (arr, guint64, i, val); + mono_array_set_internal (arr, guint64, i, val); p += 8; } break; @@ -589,35 +570,26 @@ MONO_RESTORE_WARNING case MONO_TYPE_OBJECT: case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: { - if (arr) { - HANDLE_FUNCTION_ENTER (); - MONO_HANDLE_NEW (MonoArray, arr); - - for (i = 0; i < alen; i++) { - MonoObject *item = NULL; - load_cattr_value (image, m_class_get_byval_arg (tklass), &item, p, boundp, &p, error); - if (!is_ok (error)) - return NULL; - mono_array_setref_internal (arr, i, item); - } - HANDLE_FUNCTION_RETURN (); - } else { - for (i = 0; i < alen; i++) { - MonoObject *item = NULL; - load_cattr_value (image, m_class_get_byval_arg (tklass), &item, p, boundp, &p, error); - if (!is_ok (error)) - return NULL; - } + HANDLE_FUNCTION_ENTER (); + MONO_HANDLE_NEW (MonoArray, arr); + + for (i = 0; i < alen; i++) { + MonoObject *item = NULL; + load_cattr_value (image, m_class_get_byval_arg (tklass), &item, p, boundp, &p, error); + if (!is_ok (error)) + return NULL; + mono_array_setref_internal (arr, i, item); } + HANDLE_FUNCTION_RETURN (); break; } default: g_error ("Type 0x%02x not handled in custom attr array decoding", basetype); } *end = p; + g_assert (out_obj); + *out_obj = (MonoObject*)arr; - if (out_obj) - *out_obj = (MonoObject*)arr; return NULL; } default: @@ -626,6 +598,236 @@ MONO_RESTORE_WARNING return NULL; } + +/* + * load_cattr_value_noalloc: + * + * Loads custom attribute values without mono allocation (invoked when AOT compiling). + * Returns MonoCustomAttrValue: + * - for primitive types: + * - (bools, ints, and enums) a pointer to the value is returned. + * - for types, the address of the loaded type is returned (DON'T FREE). + * - for strings, the address in the metadata blob is returned (DON'T FREE). + * - for arrays: + * - MonoCustomAttrValueArray* is returned. + */ +static MonoCustomAttrValue* +load_cattr_value_noalloc (MonoImage *image, MonoType *t, const char *p, const char *boundp, const char **end, MonoError *error) +{ + int type = t->type; + guint32 slen; + MonoClass *tklass = t->data.klass; + MonoCustomAttrValue* result = (MonoCustomAttrValue *)g_malloc (sizeof (MonoCustomAttrValue)); + + g_assert (boundp); + error_init (error); + + if (type == MONO_TYPE_GENERICINST) { + MonoGenericClass * mgc = t->data.generic_class; + MonoClass * cc = mgc->container_class; + if (m_class_is_enumtype (cc)) { + tklass = m_class_get_element_class (cc); + t = m_class_get_byval_arg (tklass); + type = t->type; + } else { + g_error ("Unhandled type of generic instance in load_cattr_value_noalloc: %s", m_class_get_name (cc)); + } + } + result->type = type; + +handle_enum: + switch (type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: { + MonoBoolean *bval = (MonoBoolean *)g_malloc (sizeof (MonoBoolean)); + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + *bval = *p; + *end = p + 1; + result->value.primitive = bval; + return result; + } + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + case MONO_TYPE_I2: { + guint16 *val = (guint16 *)g_malloc (sizeof (guint16)); + if (!bcheck_blob (p, 1, boundp, error)) + return NULL; + *val = read16 (p); + *end = p + 2; + result->value.primitive = val; + return result; + } +#if SIZEOF_VOID_P == 4 + case MONO_TYPE_U: + case MONO_TYPE_I: +#endif + case MONO_TYPE_R4: + case MONO_TYPE_U4: + case MONO_TYPE_I4: { + guint32 *val = (guint32 *)g_malloc (sizeof (guint32)); + if (!bcheck_blob (p, 3, boundp, error)) + return NULL; + *val = read32 (p); + *end = p + 4; + result->value.primitive = val; + return result; + } +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_U: /* error out instead? this should probably not happen */ + case MONO_TYPE_I: +#endif + case MONO_TYPE_U8: + case MONO_TYPE_I8: { + guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + *val = read64 (p); + *end = p + 8; + result->value.primitive = val; + return result; + } + case MONO_TYPE_R8: { + double *val = (double *)g_malloc (sizeof (double)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + readr8 (p, val); + *end = p + 8; + result->value.primitive = val; + return result; + } + case MONO_TYPE_VALUETYPE: + if (m_class_is_enumtype (t->data.klass)) { + type = mono_class_enum_basetype_internal (t->data.klass)->type; + goto handle_enum; + } else { + MonoClass *k = t->data.klass; + + if (mono_is_corlib_image (m_class_get_image (k)) && strcmp (m_class_get_name_space (k), "System") == 0 && strcmp (m_class_get_name (k), "DateTime") == 0){ + guint64 *val = (guint64 *)g_malloc (sizeof (guint64)); + if (!bcheck_blob (p, 7, boundp, error)) + return NULL; + *val = read64 (p); + *end = p + 8; + result->value.primitive = val; + return result; + } + } + g_error ("generic valutype %s not handled in custom attr value decoding", m_class_get_name (t->data.klass)); + break; + + case MONO_TYPE_STRING: { + const char *start = p; + + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; +MONO_DISABLE_WARNING (4310) // cast truncates constant value + if (*p == (char)0xFF) { + *end = p + 1; + return NULL; + } +MONO_RESTORE_WARNING + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + *end = p + slen; + result->value.primitive = (gpointer)start; + return result; + } + case MONO_TYPE_CLASS: { + result->value.primitive = load_cattr_type (image, t, TRUE, p, boundp, end, error, &slen);; + return result; + } + case MONO_TYPE_OBJECT: { + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + char subt = *p++; + MonoClass *subc = NULL; + + if (subt == CATTR_TYPE_SYSTEM_TYPE) { + result->value.primitive = load_cattr_type (image, t, FALSE, p, boundp, end, error, &slen); + return result; + } else if (subt == 0x0E) { + type = MONO_TYPE_STRING; + goto handle_enum; + } else if (subt == 0x1D) { + MonoType simple_type = {{0}}; + if (!bcheck_blob (p, 0, boundp, error)) + return NULL; + int etype = *p; + p ++; + + type = MONO_TYPE_SZARRAY; + if (etype == CATTR_TYPE_SYSTEM_TYPE) { + tklass = mono_defaults.systemtype_class; + } else if (etype == MONO_TYPE_ENUM) { + tklass = load_cattr_enum_type (image, p, boundp, &p, error); + if (!is_ok (error)) + return NULL; + } else { + if (etype == CATTR_BOXED_VALUETYPE_PREFIX) + /* See Partition II, Appendix B3 */ + etype = MONO_TYPE_OBJECT; + simple_type.type = (MonoTypeEnum)etype; + tklass = mono_class_from_mono_type_internal (&simple_type); + } + goto handle_enum; + } else if (subt == MONO_TYPE_ENUM) { + char *n; + MonoType *enum_type; + if (!decode_blob_value_checked (p, boundp, &slen, &p, error)) + return NULL; + if (slen > 0 && !bcheck_blob (p, slen - 1, boundp, error)) + return NULL; + n = (char *)g_memdup (p, slen + 1); + n [slen] = 0; + enum_type = cattr_type_from_name (n, image, FALSE, error); + g_free (n); + return_val_if_nok (error, NULL); + p += slen; + subc = mono_class_from_mono_type_internal (enum_type); + } else if (subt >= MONO_TYPE_BOOLEAN && subt <= MONO_TYPE_R8) { + MonoType simple_type = {{0}}; + simple_type.type = (MonoTypeEnum)subt; + subc = mono_class_from_mono_type_internal (&simple_type); + } else { + g_error ("Unknown type 0x%02x for object type encoding in custom attr", subt); + } + result->value.primitive = load_cattr_value_noalloc (image, m_class_get_byval_arg (subc), p, boundp, end, error); + return result; + } + case MONO_TYPE_SZARRAY: { + guint32 i, alen; + + if (!bcheck_blob (p, 3, boundp, error)) + return NULL; + alen = read32 (p); + p += 4; + if (alen == 0xffffffff) { + *end = p; + return NULL; + } + + result->value.array = g_malloc (sizeof (MonoCustomAttrValueArray) + alen * sizeof (MonoCustomAttrValue)); + result->value.array->len = alen; + + for (i = 0; i < alen; i++) { + MonoCustomAttrValue* array_element = load_cattr_value_noalloc (image, m_class_get_byval_arg (tklass), p, boundp, &p, error); + if (!is_ok (error)) + return NULL; + result->value.array->values[i] = *array_element; + } + *end = p; + return result; + } + default: + g_error ("Type 0x%02x not handled in custom attr value decoding", type); + } + return NULL; +} + static MonoObject* load_cattr_value_boxed (MonoDomain *domain, MonoImage *image, MonoType *t, const char* p, const char *boundp, const char** end, MonoError *error) { @@ -1219,37 +1421,81 @@ mono_reflection_create_custom_attr_data_args (MonoImage *image, MonoMethod *meth *named_arg_info = NULL; } +/* + * free_decoded_custom_attr: + * + * Handles freeing of MonoCustomAttrValue type properly. + * Strings and MonoType* are not freed since they come from the metadata. + */ +static void +free_decoded_custom_attr(MonoCustomAttrValue* cattr_val) +{ + if (!cattr_val) + return; + + if (cattr_val->type == MONO_TYPE_SZARRAY) { + // attribute parameter types only support single-dimensional arrays + for (int i = 0; i < cattr_val->value.array->len; i++) { + if (cattr_val->value.array->values[i].type != MONO_TYPE_STRING && cattr_val->value.array->values[i].type != MONO_TYPE_CLASS) + g_free(cattr_val->value.array->values[i].value.primitive); + } + g_free (cattr_val->value.array); + } else if (cattr_val->type != MONO_TYPE_STRING && cattr_val->type != MONO_TYPE_CLASS) { + g_free (cattr_val->value.primitive); + } +} + +/* + * mono_reflection_free_custom_attr_data_args_noalloc: + * + * Frees up MonoDecodeCustomAttr type. + * Must be called after mono_reflection_create_custom_attr_data_args_noalloc to properly free up allocated struct. + */ +void +mono_reflection_free_custom_attr_data_args_noalloc (MonoDecodeCustomAttr* decoded_args) +{ + if (!decoded_args) + return; + + // free typed args + for (int i = 0; i < decoded_args->typed_args_num; i++) { + free_decoded_custom_attr(decoded_args->typed_args[i]); + g_free(decoded_args->typed_args[i]); + } + g_free(decoded_args->typed_args); + + // free named args + for (int i = 0; i < decoded_args->named_args_num; i++) { + free_decoded_custom_attr(decoded_args->named_args[i]); + g_free(decoded_args->named_args[i]); + } + g_free(decoded_args->named_args); + + // free named args info + g_free(decoded_args->named_args_info); + + g_free(decoded_args); +} + /* * mono_reflection_create_custom_attr_data_args_noalloc: * - * Same as mono_reflection_create_custom_attr_data_args but allocate no managed objects, return values - * using C arrays. Only usable for cattrs with primitive/type/string arguments. - * For types, a MonoType* is returned. - * For strings, the address in the metadata blob is returned. - * For arrays, NULL is returned. - * TYPED_ARGS, NAMED_ARGS, and NAMED_ARG_INFO should be freed using g_free (). + * Same as mono_reflection_create_custom_attr_data_args but allocate no managed objects. + * Returns MonoDecodeCustomAttr struct with information about typed and named arguments. + * Typed and named arguments are represented as array of MonoCustomAttrValue. + * Return value must be freed using mono_reflection_free_custom_attr_data_args_noalloc (). */ -void -mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, - gpointer **typed_args_out, gpointer **named_args_out, int *num_named_args, - CattrNamedArg **named_arg_info, MonoError *error) +MonoDecodeCustomAttr* +mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMethod *method, const guchar *data, guint32 len, MonoError *error) { - gpointer *typed_args, *named_args; MonoClass *attrklass; const char *p = (const char*)data; const char *data_end = p + len; const char *named; guint32 i, j, num_named; - CattrNamedArg *arginfo = NULL; + MonoDecodeCustomAttr *decoded_args = g_malloc0 (sizeof (MonoDecodeCustomAttr)); MonoMethodSignature *sig = mono_method_signature_internal (method); - *typed_args_out = NULL; - *named_args_out = NULL; - *named_arg_info = NULL; - - typed_args = NULL; - named_args = NULL; - error_init (error); mono_class_init_internal (method->klass); @@ -1260,11 +1506,11 @@ mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMeth /* skip prolog */ p += 2; - typed_args = g_new0 (gpointer, sig->param_count); - + decoded_args->typed_args_num = sig->param_count; + decoded_args->typed_args = g_malloc0 (sig->param_count * sizeof (MonoCustomAttrValue*)); for (i = 0; i < sig->param_count; ++i) { - typed_args [i] = load_cattr_value (image, sig->params [i], NULL, p, data_end, &p, error); - return_if_nok (error); + decoded_args->typed_args [i] = load_cattr_value_noalloc (image, sig->params [i], p, data_end, &p, error); + return_val_if_nok (error, NULL); } named = p; @@ -1273,14 +1519,15 @@ mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMeth if (!bcheck_blob (named, 1, data_end, error)) goto fail; num_named = read16 (named); - named_args = g_new0 (gpointer, num_named); - return_if_nok (error); + + decoded_args->named_args_num = num_named; + decoded_args->named_args = g_malloc0 (num_named * sizeof (MonoCustomAttrValue*)); + + return_val_if_nok (error, NULL); named += 2; attrklass = method->klass; - arginfo = g_new0 (CattrNamedArg, num_named); - *named_arg_info = arginfo; - *num_named_args = num_named; + decoded_args->named_args_info = g_new0 (CattrNamedArg, num_named); /* Parse each named arg, and add to arginfo. Each named argument could * be a field name or a property name followed by a value. */ @@ -1329,10 +1576,11 @@ mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMeth goto fail; } - arginfo [j].type = field->type; - arginfo [j].field = field; + decoded_args->named_args_info [j].type = field->type; + decoded_args->named_args_info [j].field = field; + + decoded_args->named_args [j] = load_cattr_value_noalloc (image, field->type, named, data_end, &named, error); - named_args [j] = load_cattr_value (image, field->type, NULL, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); goto fail; @@ -1350,10 +1598,10 @@ mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMeth prop_type = prop->get? mono_method_signature_internal (prop->get)->ret : mono_method_signature_internal (prop->set)->params [mono_method_signature_internal (prop->set)->param_count - 1]; - arginfo [j].type = prop_type; - arginfo [j].prop = prop; + decoded_args->named_args_info [j].type = prop_type; + decoded_args->named_args_info [j].prop = prop; - named_args [j] = load_cattr_value (image, prop_type, NULL, named, data_end, &named, error); + decoded_args->named_args [j] = load_cattr_value_noalloc (image, prop_type, named, data_end, &named, error); if (!is_ok (error)) { g_free (name); goto fail; @@ -1362,15 +1610,11 @@ mono_reflection_create_custom_attr_data_args_noalloc (MonoImage *image, MonoMeth g_free (name); } - *typed_args_out = typed_args; - *named_args_out = named_args; - return; + return decoded_args; fail: mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Binary format of the specified custom attribute was invalid."); - g_free (typed_args); - g_free (named_args); - g_free (arginfo); - *named_arg_info = NULL; + mono_reflection_free_custom_attr_data_args_noalloc(decoded_args); + return NULL; } void diff --git a/src/mono/mono/metadata/marshal-shared.c b/src/mono/mono/metadata/marshal-shared.c index 9d8f37bfa86346..7c67f9dd3fe363 100644 --- a/src/mono/mono/metadata/marshal-shared.c +++ b/src/mono/mono/metadata/marshal-shared.c @@ -218,20 +218,14 @@ mono_marshal_shared_get_fixed_buffer_attr (MonoClassField *field, MonoType **out } } if (attr) { - gpointer *typed_args, *named_args; - CattrNamedArg *arginfo; - int num_named_args; - - mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, - &typed_args, &named_args, &num_named_args, &arginfo, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); if (!is_ok (error)) return FALSE; - *out_etype = (MonoType*)typed_args [0]; - *out_len = *(gint32*)typed_args [1]; - g_free (typed_args [1]); - g_free (typed_args); - g_free (named_args); - g_free (arginfo); + + *out_etype = (MonoType*)decoded_args->typed_args[0]->value.primitive; + *out_len = *(gint32*)decoded_args->typed_args[1]->value.primitive; + + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); } if (cinfo && !cinfo->cached) mono_custom_attrs_free (cinfo); diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 5f59ef95e22219..96e67f123eec8a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3350,6 +3350,82 @@ mono_marshal_set_callconv_from_unmanaged_callconv_attribute (MonoMethod *method, mono_custom_attrs_free(cinfo); } +static void +mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, MonoType *cmod_type, MonoError *error) +{ + g_assert (cmod_type->type == MONO_TYPE_CLASS); + MonoClass *cmod_klass = mono_class_from_mono_type_internal (cmod_type); + g_assert (m_class_get_image (cmod_klass) == mono_defaults.corlib); + g_assert (!strcmp (m_class_get_name_space (cmod_klass), "System.Runtime.CompilerServices")); + + const char *name = m_class_get_name (cmod_klass); + g_assert (g_str_has_prefix (name, "CallConv")); + + name += strlen ("CallConv"); + if (!strcmp (name, "Cdecl")) + sig->call_convention = MONO_CALL_C; + else if (!strcmp (name, "Stdcall")) + sig->call_convention = MONO_CALL_STDCALL; + else if (!strcmp (name, "Thiscall")) + sig->call_convention = MONO_CALL_THISCALL; + else if (!strcmp (name, "Fastcall")) + sig->call_convention = MONO_CALL_FASTCALL; + else if (!strcmp (name, "SuppressGCTransition")) + sig->suppress_gc_transition = 1; + // TODO: Support CallConvMemberFunction? + // TODO: Support multiple calling convetions? + // - Switch MonoCallConvention enum values to powers of 2 + // - Adjust the code above with 'anding' the attribute parameter value +} + +static void +mono_marshal_set_callconv_from_unmanaged_callers_only_attribute (MonoMethod *method, MonoMethodSignature *csig) +{ + MonoClass *attr_class = mono_class_try_get_unmanaged_callers_only_attribute_class (); + if (!attr_class) + return; + + ERROR_DECL (error); + MonoCustomAttrInfo *cinfo = mono_custom_attrs_from_method_checked (method, error); + if (!is_ok (error) || !cinfo) { + mono_error_cleanup (error); + return; + } + + mono_array_size_t i; + MonoCustomAttrEntry *attr = NULL; + for (i = 0; i < cinfo->num_attrs; ++i) { + MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; + if (ctor_class == attr_class) { + attr = &cinfo->attrs [i]; + break; + } + } + + if (attr != NULL) { + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); + mono_error_assert_ok (error); + for (i = 0; i < decoded_args->named_args_num; ++i) { + if (decoded_args->named_args_info [i].field && !strcmp (decoded_args->named_args_info [i].field->name, "CallConvs")) { + g_assertf(decoded_args->named_args_info [i].field->type->type == MONO_TYPE_SZARRAY, "UnmanagedCallersOnlyAttribute parameter %s must be an array, specified for method %s", decoded_args->named_args_info [i].field->name, method->name); + MonoCustomAttrValueArray *calling_conventions = decoded_args->named_args[i]->value.array; + if (calling_conventions->len > 0) { + if (calling_conventions->len > 1) + g_warning ("Multiple calling conventions are not supported for UnmanagedCallersOnlyAttribute parameter %s, specified for method %s. Only the first calling convention will be taken into account", decoded_args->named_args_info [i].field->name, method->name); + // TODO: Support multiple conventions? + MonoType* calling_convention = (MonoType*)calling_conventions->values[0].value.primitive; + mono_marshal_set_signature_callconv_from_attribute (csig, calling_convention, error); + } + } + } + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); + } + + if (!cinfo->cached) + mono_custom_attrs_free(cinfo); +} + + /** * mono_marshal_get_native_wrapper: * \param method The \c MonoMethod to wrap. @@ -4045,6 +4121,8 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, if (invoke) mono_marshal_set_callconv_from_modopt (invoke, csig, TRUE); + else + mono_marshal_set_callconv_from_unmanaged_callers_only_attribute(method, csig); /* The attribute is only available in Net 2.0 */ if (delegate_klass && mono_class_try_get_unmanaged_function_pointer_attribute_class ()) { @@ -4069,28 +4147,23 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, } } if (attr) { - gpointer *typed_args, *named_args; - CattrNamedArg *arginfo; gint32 call_conv; gint32 charset = 0; MonoBoolean set_last_error = 0; - int num_named_args; - - mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, - &typed_args, &named_args, &num_named_args, &arginfo, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, attr->ctor, attr->data, attr->data_size, error); g_assert (is_ok (error)); /* typed args */ - call_conv = *(gint32*)typed_args [0]; + call_conv = *(gint32*)decoded_args->typed_args[0]->value.primitive; /* named args */ - for (i = 0; i < num_named_args; ++i) { - CattrNamedArg *narg = &arginfo [i]; - + for (i = 0; i < decoded_args->named_args_num; ++i) { + CattrNamedArg *narg = &decoded_args->named_args_info[i]; g_assert (narg->field); + if (!strcmp (narg->field->name, "CharSet")) { - charset = *(gint32*)named_args [i]; + charset = *(gint32*)decoded_args->named_args [i]->value.primitive; } else if (!strcmp (narg->field->name, "SetLastError")) { - set_last_error = *(MonoBoolean*)named_args [i]; + set_last_error = *(MonoBoolean*)decoded_args->named_args [i]->value.primitive; } else if (!strcmp (narg->field->name, "BestFitMapping")) { // best_fit_mapping = *(MonoBoolean*)mono_object_unbox_internal (o); } else if (!strcmp (narg->field->name, "ThrowOnUnmappableChar")) { @@ -4098,12 +4171,8 @@ mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, } else { g_assert_not_reached (); } - g_free (named_args [i]); } - g_free (typed_args [0]); - g_free (typed_args); - g_free (named_args); - g_free (arginfo); + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); memset (&piinfo, 0, sizeof (piinfo)); m.piinfo = &piinfo; diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 9b86e48b3e68cb..c912d769508999 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -944,22 +944,14 @@ get_dllimportsearchpath_flags (MonoCustomAttrInfo *cinfo) if (!attr) return -3; - gpointer *typed_args, *named_args; - CattrNamedArg *arginfo; - int num_named_args; - - mono_reflection_create_custom_attr_data_args_noalloc (m_class_get_image (attr->ctor->klass), attr->ctor, attr->data, attr->data_size, - &typed_args, &named_args, &num_named_args, &arginfo, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (m_class_get_image (attr->ctor->klass), attr->ctor, attr->data, attr->data_size, error); if (!is_ok (error)) { mono_error_cleanup (error); return -4; } - flags = *(gint32*)typed_args [0]; - g_free (typed_args [0]); - g_free (typed_args); - g_free (named_args); - g_free (arginfo); + flags = *(gint32*)decoded_args->typed_args[0]->value.primitive; + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); return flags; } diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 3ade4cce92ba9f..2f84f35f79aaa0 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -5188,23 +5188,18 @@ MONO_RESTORE_WARNING exit (1); } - gpointer *typed_args = NULL; - gpointer *named_args = NULL; - CattrNamedArg *named_arg_info = NULL; - int num_named_args = 0; - mono_reflection_create_custom_attr_data_args_noalloc (acfg->image, e->ctor, e->data, e->data_size, &typed_args, &named_args, &num_named_args, &named_arg_info, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (acfg->image, e->ctor, e->data, e->data_size, error); mono_error_assert_ok (error); - for (j = 0; j < num_named_args; ++j) { - if (named_arg_info [j].field && !strcmp (named_arg_info [j].field->name, "EntryPoint")) { - named = named_args [j]; + for (j = 0; j < decoded_args->named_args_num; ++j) { + if (decoded_args->named_args_info [j].field && !strcmp (decoded_args->named_args_info [j].field->name, "EntryPoint")) { + named = (const char *)decoded_args->named_args[j]->value.primitive; slen = mono_metadata_decode_value (named, &named); export_name = (char *)g_malloc (slen + 1); memcpy (export_name, named, slen); export_name [slen] = 0; } } - g_free (named_args); - g_free (named_arg_info); + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); wrapper = mono_marshal_get_managed_wrapper (method, NULL, 0, error); mono_error_assert_ok (error); diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c index 37c892d91ca04d..c77fdb92675f47 100644 --- a/src/mono/mono/mini/driver.c +++ b/src/mono/mono/mini/driver.c @@ -429,22 +429,14 @@ method_should_be_regression_tested (MonoMethod *method, gboolean interp) if (strcmp (m_class_get_name (klass), "CategoryAttribute") || mono_method_signature_internal (centry->ctor)->param_count != 1) continue; - gpointer *typed_args, *named_args; - int num_named_args; - CattrNamedArg *arginfo; - - mono_reflection_create_custom_attr_data_args_noalloc ( - mono_defaults.corlib, centry->ctor, centry->data, centry->data_size, - &typed_args, &named_args, &num_named_args, &arginfo, error); + MonoDecodeCustomAttr *decoded_args = mono_reflection_create_custom_attr_data_args_noalloc (mono_defaults.corlib, centry->ctor, centry->data, centry->data_size, error); if (!is_ok (error)) continue; - const char *arg = (const char*)typed_args [0]; + const char *arg = (const char*)decoded_args->typed_args[0]->value.primitive; mono_metadata_decode_value (arg, &arg); char *utf8_str = (char*)arg; //this points into image memory that is constant - g_free (typed_args); - g_free (named_args); - g_free (arginfo); + mono_reflection_free_custom_attr_data_args_noalloc (decoded_args); if (interp && !strcmp (utf8_str, "!INTERPRETER")) { g_print ("skip %s...\n", method->name); diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 2d9e84209b2cfd..6d66d38a5a1038 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(PInvoke/SafeHandles) add_subdirectory(PInvoke/Vector2_3_4) add_subdirectory(UnmanagedCallConv) add_subdirectory(UnmanagedCallersOnly) +add_subdirectory(UnmanagedCallersOnly_MonoAot) add_subdirectory(PrimitiveMarshalling/Bool) add_subdirectory(PrimitiveMarshalling/UIntPtr) add_subdirectory(ArrayMarshalling/BoolArray) diff --git a/src/tests/Interop/UnmanagedCallersOnly_MonoAot/CMakeLists.txt b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/CMakeLists.txt new file mode 100644 index 00000000000000..719a46c9fba938 --- /dev/null +++ b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/CMakeLists.txt @@ -0,0 +1,10 @@ +project (UnmanagedCallersOnly_MonoAotDll) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") +set(SOURCES UnmanagedCallersOnly_MonoAotDll.cpp ) + +# add the executable +add_library (UnmanagedCallersOnly_MonoAotDll SHARED ${SOURCES}) +target_link_libraries(UnmanagedCallersOnly_MonoAotDll ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS UnmanagedCallersOnly_MonoAotDll DESTINATION bin) diff --git a/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotDll.cpp b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotDll.cpp new file mode 100644 index 00000000000000..d3ead331faf9dc --- /dev/null +++ b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotDll.cpp @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +typedef int (__stdcall *CALLBACKPROC_STDCALL)(int n); +typedef int (__cdecl *CALLBACKPROC_CDECL)(int n); + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE CallManagedProc_Stdcall(CALLBACKPROC_STDCALL pCallbackProc, int n) +{ + return pCallbackProc(n); +} + +extern "C" DLL_EXPORT int STDMETHODCALLTYPE CallManagedProc_Cdecl(CALLBACKPROC_CDECL pCallbackProc, int n) +{ + return pCallbackProc(n); +} diff --git a/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.cs b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.cs new file mode 100644 index 00000000000000..60c7412307ef81 --- /dev/null +++ b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using Xunit; + +// Stripped-down variant of Interop/UnmanagedCallersOnly/* test used for testing Mono AOT support for UnmanagedCallersOnly attribute +public unsafe class Program +{ + public static class UnmanagedCallersOnly_MonoAotDll + { + [DllImport(nameof(UnmanagedCallersOnly_MonoAotDll))] + public static extern int CallManagedProc_Stdcall(delegate* unmanaged[Stdcall] callbackProc, int n); + + [DllImport(nameof(UnmanagedCallersOnly_MonoAotDll))] + public static extern int CallManagedProc_Cdecl(delegate* unmanaged[Cdecl] callbackProc, int n); + } + + public static int Main(string[] args) + { + var result = 100; + + result += TestUnmanagedCallersOnlyValid_CallConvStdcall(); + result += TestUnmanagedCallersOnlyValid_CallConvCdecl(); + + return result; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + public static int ManagedDoubleCallback_Stdcall(int n) + { + return DoubleImpl(n); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + public static int ManagedDoubleCallback_Cdecl(int n) + { + return DoubleImpl(n); + } + + private static int DoubleImpl(int n) + { + return 2 * n; + } + + public static int TestUnmanagedCallersOnlyValid_CallConvStdcall() + { + Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyValid_CallConvStdcall)}..."); + + int n = 12345; + int expected = DoubleImpl(n); + int actual = UnmanagedCallersOnly_MonoAotDll.CallManagedProc_Stdcall(&ManagedDoubleCallback_Stdcall, n); + + return expected == actual ? 0 : -1; + } + + public static int TestUnmanagedCallersOnlyValid_CallConvCdecl() + { + Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyValid_CallConvCdecl)}..."); + + int n = 12345; + int expected = DoubleImpl(n); + int actual = UnmanagedCallersOnly_MonoAotDll.CallManagedProc_Cdecl(&ManagedDoubleCallback_Cdecl, n); + + return expected == actual ? 0 : -1; + } +} diff --git a/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.csproj b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.csproj new file mode 100644 index 00000000000000..8932cacf53f7cd --- /dev/null +++ b/src/tests/Interop/UnmanagedCallersOnly_MonoAot/UnmanagedCallersOnly_MonoAotTest.csproj @@ -0,0 +1,14 @@ + + + Exe + true + + + + + + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 072fe62e989e76..060df6ce10c4c7 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3338,6 +3338,9 @@ https://github.com/dotnet/runtime/issues/41519 + + https://github.com/dotnet/runtime/issues/41519 + https://github.com/dotnet/runtime/issues/41472 @@ -3701,6 +3704,9 @@ needs triage + + needs triage + needs triage