2 #include <mono/metadata/object.h>
3 #include <mono/metadata/verify.h>
4 #include <mono/metadata/tabledefs.h>
5 #include <mono/metadata/mono-endian.h>
11 * Pull the list of opcodes
13 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
17 #include "mono/cil/opcode.def"
23 mono_free_verify_list (GSList *list)
28 for (tmp = list; tmp; tmp = tmp->next) {
30 g_free (info->message);
36 #define ADD_ERROR(list,msg) \
38 MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \
39 vinfo->status = MONO_VERIFY_ERROR; \
40 vinfo->message = (msg); \
41 (list) = g_slist_prepend ((list), vinfo); \
44 #define ADD_WARN(list,code,msg) \
46 MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \
47 vinfo->status = (code); \
48 vinfo->message = (msg); \
49 (list) = g_slist_prepend ((list), vinfo); \
54 "ar-SA", "ar-IQ", "ar-EG", "ar-LY",
55 "ar-DZ", "ar-MA", "ar-TN", "ar-OM",
56 "ar-YE", "ar-SY", "ar-JO", "ar-LB",
57 "ar-KW", "ar-AE", "ar-BH", "ar-QA",
58 "bg-BG", "ca-ES", "zh-TW", "zh-CN",
59 "zh-HK", "zh-SG", "zh-MO", "cs-CZ",
60 "da-DK", "de-DE", "de-CH", "de-AT",
61 "de-LU", "de-LI", "el-GR", "en-US",
62 "en-GB", "en-AU", "en-CA", "en-NZ",
63 "en-IE", "en-ZA", "en-JM", "en-CB",
64 "en-BZ", "en-TT", "en-ZW", "en-PH",
65 "es-ES-Ts", "es-MX", "es-ES-Is", "es-GT",
66 "es-CR", "es-PA", "es-DO", "es-VE",
67 "es-CO", "es-PE", "es-AR", "es-EC",
68 "es-CL", "es-UY", "es-PY", "es-BO",
69 "es-SV", "es-HN", "es-NI", "es-PR",
70 "Fi-FI", "fr-FR", "fr-BE", "fr-CA",
71 "Fr-CH", "fr-LU", "fr-MC", "he-IL",
72 "hu-HU", "is-IS", "it-IT", "it-CH",
73 "Ja-JP", "ko-KR", "nl-NL", "nl-BE",
74 "nb-NO", "nn-NO", "pl-PL", "pt-BR",
75 "pt-PT", "ro-RO", "ru-RU", "hr-HR",
76 "Lt-sr-SP", "Cy-sr-SP", "sk-SK", "sq-AL",
77 "sv-SE", "sv-FI", "th-TH", "tr-TR",
78 "ur-PK", "id-ID", "uk-UA", "be-BY",
79 "sl-SI", "et-EE", "lv-LV", "lt-LT",
80 "fa-IR", "vi-VN", "hy-AM", "Lt-az-AZ",
82 "eu-ES", "mk-MK", "af-ZA",
83 "ka-GE", "fo-FO", "hi-IN", "ms-MY",
84 "ms-BN", "kk-KZ", "ky-KZ", "sw-KE",
85 "Lt-uz-UZ", "Cy-uz-UZ", "tt-TA", "pa-IN",
86 "gu-IN", "ta-IN", "te-IN", "kn-IN",
87 "mr-IN", "sa-IN", "mn-MN", "gl-ES",
88 "kok-IN", "syr-SY", "div-MV",
93 is_valid_culture (const char *cname)
99 for (i = 0; !found && valid_cultures [i]; ++i) {
100 if (g_strcasecmp (valid_cultures [i], cname))
107 is_valid_assembly_flags (guint32 flags) {
108 /* Metadata: 22.1.2 */
109 flags &= ~(0x8000 | 0x4000); /* ignore reserved bits 0x0030? */
110 return ((flags == 1) || (flags == 0));
114 is_valid_blob (MonoImage *image, guint32 index, int notnull)
117 const char *p, *send;
119 if (index >= image->heap_blob.size)
121 p = mono_metadata_blob_heap (image, index);
122 size = mono_metadata_decode_blob_size (p, &send);
123 if (index + size + (send-p) > image->heap_blob.size)
125 if (notnull && !size)
131 is_valid_string (MonoImage *image, guint32 index, int notnull)
133 const char *p, *send, *res;
135 if (index >= image->heap_strings.size)
137 res = p = mono_metadata_string_heap (image, index);
138 send = mono_metadata_string_heap (image, image->heap_strings.size - 1);
142 * FIXME: should check it's a valid utf8 string, too.
149 return *p? NULL: res;
153 is_valid_cls_ident (const char *p)
156 * FIXME: we need the full unicode glib support for this.
157 * Check: http://www.unicode.org/unicode/reports/tr15/Identifier.java
158 * We do the lame thing for now.
164 if (!isalnum (*p) && *p != '_')
172 is_valid_filename (const char *p)
176 return strpbrk (p, "\\//:")? 0: 1;
180 verify_assembly_table (MonoImage *image, GSList *list, int level)
182 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
183 guint32 cols [MONO_ASSEMBLY_SIZE];
186 if (level & MONO_VERIFY_ERROR) {
188 ADD_ERROR (list, g_strdup ("Assembly table may only have 0 or 1 rows"));
189 mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
191 switch (cols [MONO_ASSEMBLY_HASH_ALG]) {
192 case ASSEMBLY_HASH_NONE:
193 case ASSEMBLY_HASH_MD5:
194 case ASSEMBLY_HASH_SHA1:
197 ADD_ERROR (list, g_strdup_printf ("Hash algorithm 0x%x unknown", cols [MONO_ASSEMBLY_HASH_ALG]));
200 if (!is_valid_assembly_flags (cols [MONO_ASSEMBLY_FLAGS]))
201 ADD_ERROR (list, g_strdup_printf ("Invalid flags in assembly: 0x%x", cols [MONO_ASSEMBLY_FLAGS]));
203 if (!is_valid_blob (image, cols [MONO_ASSEMBLY_PUBLIC_KEY], FALSE))
204 ADD_ERROR (list, g_strdup ("Assembly public key is an invalid index"));
206 if (!(p = is_valid_string (image, cols [MONO_ASSEMBLY_NAME], TRUE))) {
207 ADD_ERROR (list, g_strdup ("Assembly name is invalid"));
209 if (strpbrk (p, ":\\/."))
210 ADD_ERROR (list, g_strdup_printf ("Assembly name `%s' contains invalid chars", p));
213 if (!(p = is_valid_string (image, cols [MONO_ASSEMBLY_CULTURE], FALSE))) {
214 ADD_ERROR (list, g_strdup ("Assembly culture is an invalid index"));
216 if (!is_valid_culture (p))
217 ADD_ERROR (list, g_strdup_printf ("Assembly culture `%s' is invalid", p));
224 verify_assemblyref_table (MonoImage *image, GSList *list, int level)
226 MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF];
227 guint32 cols [MONO_ASSEMBLYREF_SIZE];
231 if (level & MONO_VERIFY_ERROR) {
232 for (i = 0; i < t->rows; ++i) {
233 mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE);
234 if (!is_valid_assembly_flags (cols [MONO_ASSEMBLYREF_FLAGS]))
235 ADD_ERROR (list, g_strdup_printf ("Invalid flags in assemblyref row %d: 0x%x", i + 1, cols [MONO_ASSEMBLY_FLAGS]));
237 if (!is_valid_blob (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], FALSE))
238 ADD_ERROR (list, g_strdup_printf ("AssemblyRef public key in row %d is an invalid index", i + 1));
240 if (!(p = is_valid_string (image, cols [MONO_ASSEMBLYREF_CULTURE], FALSE))) {
241 ADD_ERROR (list, g_strdup_printf ("AssemblyRef culture in row %d is invalid", i + 1));
243 if (!is_valid_culture (p))
244 ADD_ERROR (list, g_strdup_printf ("AssemblyRef culture `%s' in row %d is invalid", p, i + 1));
247 if (cols [MONO_ASSEMBLYREF_HASH_VALUE] && !is_valid_blob (image, cols [MONO_ASSEMBLYREF_HASH_VALUE], TRUE))
248 ADD_ERROR (list, g_strdup_printf ("AssemblyRef hash value in row %d is invalid or not null and empty", i + 1));
251 if (level & MONO_VERIFY_WARNING) {
252 /* check for duplicated rows */
253 for (i = 0; i < t->rows; ++i) {
260 verify_class_layout_table (MonoImage *image, GSList *list, int level)
262 MonoTableInfo *t = &image->tables [MONO_TABLE_CLASSLAYOUT];
263 MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF];
264 guint32 cols [MONO_CLASS_LAYOUT_SIZE];
267 if (level & MONO_VERIFY_ERROR) {
268 for (i = 0; i < t->rows; ++i) {
269 mono_metadata_decode_row (t, i, cols, MONO_CLASS_LAYOUT_SIZE);
271 if (cols [MONO_CLASS_LAYOUT_PARENT] > tdef->rows || !cols [MONO_CLASS_LAYOUT_PARENT]) {
272 ADD_ERROR (list, g_strdup_printf ("Parent in class layout is invalid in row %d", i + 1));
274 value = mono_metadata_decode_row_col (tdef, cols [MONO_CLASS_LAYOUT_PARENT] - 1, MONO_TYPEDEF_FLAGS);
275 if (value & TYPE_ATTRIBUTE_INTERFACE)
276 ADD_ERROR (list, g_strdup_printf ("Parent in class layout row %d is an interface", i + 1));
277 if (value & TYPE_ATTRIBUTE_AUTO_LAYOUT)
278 ADD_ERROR (list, g_strdup_printf ("Parent in class layout row %d is AutoLayout", i + 1));
279 if (value & TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT) {
280 switch (cols [MONO_CLASS_LAYOUT_PACKING_SIZE]) {
281 case 0: case 1: case 2: case 4: case 8: case 16:
282 case 32: case 64: case 128: break;
284 ADD_ERROR (list, g_strdup_printf ("Packing size %d in class layout row %d is invalid", cols [MONO_CLASS_LAYOUT_PACKING_SIZE], i + 1));
286 } else if (value & TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) {
288 * FIXME: LAMESPEC: it claims it must be 0 (it's 1, instead).
289 if (cols [MONO_CLASS_LAYOUT_PACKING_SIZE])
290 ADD_ERROR (list, g_strdup_printf ("Packing size %d in class layout row %d is invalid with explicit layout", cols [MONO_CLASS_LAYOUT_PACKING_SIZE], i + 1));
294 * FIXME: we need to check that if class size != 0,
295 * it needs to be greater than the class calculated size.
296 * If parent is a valuetype it also needs to be smaller than
297 * 1 MByte (0x100000 bytes).
298 * To do both these checks we need to load the referenced
299 * assemblies, though (the spec claims we didn't have to, bah).
302 * We need to check that the parent types have the samme layout
313 verify_constant_table (MonoImage *image, GSList *list, int level)
315 MonoTableInfo *t = &image->tables [MONO_TABLE_CONSTANT];
316 guint32 cols [MONO_CONSTANT_SIZE];
318 GHashTable *dups = g_hash_table_new (g_direct_hash, g_direct_equal);
320 for (i = 0; i < t->rows; ++i) {
321 mono_metadata_decode_row (t, i, cols, MONO_CONSTANT_SIZE);
323 if (level & MONO_VERIFY_ERROR)
324 if (g_hash_table_lookup (dups, GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT])))
325 ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is duplicated in Constant row %d", cols [MONO_CONSTANT_PARENT], i + 1));
326 g_hash_table_insert (dups, GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT]),
327 GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT]));
329 switch (cols [MONO_CONSTANT_TYPE]) {
330 case MONO_TYPE_U1: /* LAMESPEC: it says I1...*/
334 if (level & MONO_VERIFY_CLS)
335 ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Type 0x%x not CLS compliant in Constant row %d", cols [MONO_CONSTANT_TYPE], i + 1));
336 case MONO_TYPE_BOOLEAN:
344 case MONO_TYPE_STRING:
345 case MONO_TYPE_CLASS:
348 if (level & MONO_VERIFY_ERROR)
349 ADD_ERROR (list, g_strdup_printf ("Type 0x%x is invalid in Constant row %d", cols [MONO_CONSTANT_TYPE], i + 1));
351 if (level & MONO_VERIFY_ERROR) {
352 value = cols [MONO_CONSTANT_PARENT] >> HASCONSTANT_BITS;
353 switch (cols [MONO_CONSTANT_PARENT] & HASCONSTANT_MASK) {
354 case HASCONSTANT_FIEDDEF:
355 if (value > image->tables [MONO_TABLE_FIELD].rows)
356 ADD_ERROR (list, g_strdup_printf ("Parent (field) is invalid in Constant row %d", i + 1));
358 case HASCONSTANT_PARAM:
359 if (value > image->tables [MONO_TABLE_PARAM].rows)
360 ADD_ERROR (list, g_strdup_printf ("Parent (param) is invalid in Constant row %d", i + 1));
362 case HASCONSTANT_PROPERTY:
363 if (value > image->tables [MONO_TABLE_PROPERTY].rows)
364 ADD_ERROR (list, g_strdup_printf ("Parent (property) is invalid in Constant row %d", i + 1));
367 ADD_ERROR (list, g_strdup_printf ("Parent is invalid in Constant row %d", i + 1));
371 if (level & MONO_VERIFY_CLS) {
373 * FIXME: verify types is consistent with the enum type
374 * is parent is an enum.
378 g_hash_table_destroy (dups);
383 verify_event_map_table (MonoImage *image, GSList *list, int level)
385 MonoTableInfo *t = &image->tables [MONO_TABLE_EVENTMAP];
386 guint32 cols [MONO_EVENT_MAP_SIZE];
387 guint32 i, last_event;
388 GHashTable *dups = g_hash_table_new (g_direct_hash, g_direct_equal);
392 for (i = 0; i < t->rows; ++i) {
393 mono_metadata_decode_row (t, i, cols, MONO_EVENT_MAP_SIZE);
394 if (level & MONO_VERIFY_ERROR)
395 if (g_hash_table_lookup (dups, GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT])))
396 ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is duplicated in Event Map row %d", cols [MONO_EVENT_MAP_PARENT], i + 1));
397 g_hash_table_insert (dups, GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT]),
398 GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT]));
399 if (level & MONO_VERIFY_ERROR) {
400 if (cols [MONO_EVENT_MAP_PARENT] > image->tables [MONO_TABLE_TYPEDEF].rows)
401 ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is invalid in Event Map row %d", cols [MONO_EVENT_MAP_PARENT], i + 1));
402 if (cols [MONO_EVENT_MAP_EVENTLIST] > image->tables [MONO_TABLE_EVENT].rows)
403 ADD_ERROR (list, g_strdup_printf ("EventList 0x%08x is invalid in Event Map row %d", cols [MONO_EVENT_MAP_EVENTLIST], i + 1));
405 if (cols [MONO_EVENT_MAP_EVENTLIST] <= last_event)
406 ADD_ERROR (list, g_strdup_printf ("EventList overlap in Event Map row %d", i + 1));
407 last_event = cols [MONO_EVENT_MAP_EVENTLIST];
411 g_hash_table_destroy (dups);
416 verify_event_table (MonoImage *image, GSList *list, int level)
418 MonoTableInfo *t = &image->tables [MONO_TABLE_EVENT];
419 guint32 cols [MONO_EVENT_SIZE];
423 for (i = 0; i < t->rows; ++i) {
424 mono_metadata_decode_row (t, i, cols, MONO_EVENT_SIZE);
426 if (cols [MONO_EVENT_FLAGS] & ~(EVENT_SPECIALNAME|EVENT_RTSPECIALNAME)) {
427 if (level & MONO_VERIFY_ERROR)
428 ADD_ERROR (list, g_strdup_printf ("Flags 0x%04x invalid in Event row %d", cols [MONO_EVENT_FLAGS], i + 1));
430 if (!(p = is_valid_string (image, cols [MONO_EVENT_NAME], TRUE))) {
431 if (level & MONO_VERIFY_ERROR)
432 ADD_ERROR (list, g_strdup_printf ("Invalid name in Event row %d", i + 1));
434 if (level & MONO_VERIFY_CLS) {
435 if (!is_valid_cls_ident (p))
436 ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Invalid CLS name '%s` in Event row %d", p, i + 1));
440 if (level & MONO_VERIFY_ERROR && cols [MONO_EVENT_TYPE]) {
441 value = cols [MONO_EVENT_TYPE] >> TYPEDEFORREF_BITS;
442 switch (cols [MONO_EVENT_TYPE] & TYPEDEFORREF_MASK) {
443 case TYPEDEFORREF_TYPEDEF:
444 if (!value || value > image->tables [MONO_TABLE_TYPEDEF].rows)
445 ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1));
447 case TYPEDEFORREF_TYPEREF:
448 if (!value || value > image->tables [MONO_TABLE_TYPEREF].rows)
449 ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1));
451 case TYPEDEFORREF_TYPESPEC:
452 if (!value || value > image->tables [MONO_TABLE_TYPESPEC].rows)
453 ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1));
456 ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1));
460 * FIXME: check that there is 1 add and remove row in methodsemantics
461 * and 0 or 1 raise and 0 or more other (maybe it's better to check for
462 * these while checking methodsemantics).
463 * check for duplicated names for the same type [ERROR]
464 * check for CLS duplicate names for the same type [CLS]
471 verify_field_table (MonoImage *image, GSList *list, int level)
473 MonoTableInfo *t = &image->tables [MONO_TABLE_FIELD];
474 guint32 cols [MONO_FIELD_SIZE];
478 for (i = 0; i < t->rows; ++i) {
479 mono_metadata_decode_row (t, i, cols, MONO_FIELD_SIZE);
481 * Check this field has only one owner and that the owner is not
482 * an interface (done in verify_typedef_table() )
484 flags = cols [MONO_FIELD_FLAGS];
485 switch (flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) {
486 case FIELD_ATTRIBUTE_COMPILER_CONTROLLED:
487 case FIELD_ATTRIBUTE_PRIVATE:
488 case FIELD_ATTRIBUTE_FAM_AND_ASSEM:
489 case FIELD_ATTRIBUTE_ASSEMBLY:
490 case FIELD_ATTRIBUTE_FAMILY:
491 case FIELD_ATTRIBUTE_FAM_OR_ASSEM:
492 case FIELD_ATTRIBUTE_PUBLIC:
495 if (level & MONO_VERIFY_ERROR)
496 ADD_ERROR (list, g_strdup_printf ("Invalid access mask in Field row %d", i + 1));
499 if (level & MONO_VERIFY_ERROR) {
500 if ((flags & FIELD_ATTRIBUTE_LITERAL) && (flags & FIELD_ATTRIBUTE_INIT_ONLY))
501 ADD_ERROR (list, g_strdup_printf ("Literal and InitOnly cannot be both set in Field row %d", i + 1));
502 if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC))
503 ADD_ERROR (list, g_strdup_printf ("Literal needs also Static set in Field row %d", i + 1));
504 if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME))
505 ADD_ERROR (list, g_strdup_printf ("RTSpecialName needs also SpecialName set in Field row %d", i + 1));
507 * FIXME: check there is only ono owner in the respective table.
508 * if (flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL)
509 * if (flags & FIELD_ATTRIBUTE_HAS_DEFAULT)
510 * if (flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA)
513 if (!(p = is_valid_string (image, cols [MONO_FIELD_NAME], TRUE))) {
514 if (level & MONO_VERIFY_ERROR)
515 ADD_ERROR (list, g_strdup_printf ("Invalid name in Field row %d", i + 1));
517 if (level & MONO_VERIFY_CLS) {
518 if (!is_valid_cls_ident (p))
519 ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Invalid CLS name '%s` in Field row %d", p, i + 1));
524 * if owner is module needs to be static, access mask needs to be compilercontrolled,
525 * public or private (not allowed in cls mode).
526 * if owner is an enum ...
535 verify_file_table (MonoImage *image, GSList *list, int level)
537 MonoTableInfo *t = &image->tables [MONO_TABLE_FILE];
538 guint32 cols [MONO_FILE_SIZE];
541 GHashTable *dups = g_hash_table_new (g_str_hash, g_str_equal);
543 for (i = 0; i < t->rows; ++i) {
544 mono_metadata_decode_row (t, i, cols, MONO_FILE_SIZE);
545 if (level & MONO_VERIFY_ERROR) {
546 if (cols [MONO_FILE_FLAGS] != FILE_CONTAINS_METADATA && cols [MONO_FILE_FLAGS] != FILE_CONTAINS_NO_METADATA)
547 ADD_ERROR (list, g_strdup_printf ("Invalid flags in File row %d", i + 1));
548 if (!is_valid_blob (image, cols [MONO_FILE_HASH_VALUE], TRUE))
549 ADD_ERROR (list, g_strdup_printf ("File hash value in row %d is invalid or not null and empty", i + 1));
551 if (!(p = is_valid_string (image, cols [MONO_FILE_NAME], TRUE))) {
552 if (level & MONO_VERIFY_ERROR)
553 ADD_ERROR (list, g_strdup_printf ("Invalid name in File row %d", i + 1));
555 if (level & MONO_VERIFY_ERROR) {
556 if (!is_valid_filename (p))
557 ADD_ERROR (list, g_strdup_printf ("Invalid name '%s` in File row %d", p, i + 1));
558 else if (g_hash_table_lookup (dups, p)) {
559 ADD_ERROR (list, g_strdup_printf ("Duplicate name '%s` in File row %d", p, i + 1));
561 g_hash_table_insert (dups, (gpointer)p, (gpointer)p);
565 * FIXME: I don't understand what this means:
566 * If this module contains a row in the Assembly table (that is, if this module "holds the manifest")
567 * then there shall not be any row in the File table for this module - i.e., no self-reference [ERROR]
571 if (level & MONO_VERIFY_WARNING) {
572 if (!t->rows && image->tables [MONO_TABLE_EXPORTEDTYPE].rows)
573 ADD_WARN (list, MONO_VERIFY_WARNING, g_strdup ("ExportedType table should be empty if File table is empty"));
575 g_hash_table_destroy (dups);
580 verify_moduleref_table (MonoImage *image, GSList *list, int level)
582 MonoTableInfo *t = &image->tables [MONO_TABLE_MODULEREF];
583 MonoTableInfo *tfile = &image->tables [MONO_TABLE_FILE];
584 guint32 cols [MONO_MODULEREF_SIZE];
586 guint32 found, i, j, value;
587 GHashTable *dups = g_hash_table_new (g_str_hash, g_str_equal);
589 for (i = 0; i < t->rows; ++i) {
590 mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE);
591 if (!(p = is_valid_string (image, cols [MONO_MODULEREF_NAME], TRUE))) {
592 if (level & MONO_VERIFY_ERROR)
593 ADD_ERROR (list, g_strdup_printf ("Invalid name in ModuleRef row %d", i + 1));
595 if (level & MONO_VERIFY_ERROR) {
596 if (!is_valid_filename (p))
597 ADD_ERROR (list, g_strdup_printf ("Invalid name '%s` in ModuleRef row %d", p, i + 1));
598 else if (g_hash_table_lookup (dups, p)) {
599 ADD_WARN (list, MONO_VERIFY_WARNING, g_strdup_printf ("Duplicate name '%s` in ModuleRef row %d", p, i + 1));
600 g_hash_table_insert (dups, (gpointer)p, (gpointer)p);
602 for (j = 0; j < tfile->rows; ++j) {
603 value = mono_metadata_decode_row_col (tfile, j, MONO_FILE_NAME);
604 if ((pf = is_valid_string (image, value, TRUE)))
605 if (strcmp (p, pf) == 0) {
611 ADD_ERROR (list, g_strdup_printf ("Name '%s` in ModuleRef row %d doesn't have a match in File table", p, i + 1));
616 g_hash_table_destroy (dups);
621 verify_standalonesig_table (MonoImage *image, GSList *list, int level)
623 MonoTableInfo *t = &image->tables [MONO_TABLE_STANDALONESIG];
624 guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE];
628 for (i = 0; i < t->rows; ++i) {
629 mono_metadata_decode_row (t, i, cols, MONO_STAND_ALONE_SIGNATURE_SIZE);
630 if (level & MONO_VERIFY_ERROR) {
631 if (!is_valid_blob (image, cols [MONO_STAND_ALONE_SIGNATURE], TRUE)) {
632 ADD_ERROR (list, g_strdup_printf ("Signature is invalid in StandAloneSig row %d", i + 1));
634 p = mono_metadata_blob_heap (image, cols [MONO_STAND_ALONE_SIGNATURE]);
635 /* FIXME: check it's a valid locals or method sig.*/
643 mono_image_verify_tables (MonoImage *image, int level)
645 GSList *error_list = NULL;
647 error_list = verify_assembly_table (image, error_list, level);
649 * AssemblyOS, AssemblyProcessor, AssemblyRefOs and
650 * AssemblyRefProcessor should be ignored,
651 * though we may want to emit a warning, since it should not
652 * be present in a PE file.
654 error_list = verify_assemblyref_table (image, error_list, level);
655 error_list = verify_class_layout_table (image, error_list, level);
656 error_list = verify_constant_table (image, error_list, level);
658 * cutom attribute, declsecurity
660 error_list = verify_event_map_table (image, error_list, level);
661 error_list = verify_event_table (image, error_list, level);
662 error_list = verify_field_table (image, error_list, level);
663 error_list = verify_file_table (image, error_list, level);
664 error_list = verify_moduleref_table (image, error_list, level);
665 error_list = verify_standalonesig_table (image, error_list, level);
667 return g_slist_reverse (error_list);
671 TYPE_INVALID = 0, /* leave at 0. */
681 const static unsigned char
682 valid_binops [TYPE_MAX] [TYPE_MAX] = {
684 {TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_NINT, TYPE_INVALID, TYPE_MANP},
685 {TYPE_INVALID, TYPE_INVALID, TYPE_INT64, TYPE_INVALID},
686 {TYPE_INVALID, TYPE_NINT, TYPE_INVALID, TYPE_NINT, TYPE_INVALID, TYPE_MANP},
687 {TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_FLOAT},
688 {TYPE_INVALID, TYPE_MANP, TYPE_INVALID, TYPE_MANP, TYPE_INVALID, TYPE_NINT},
690 /* int32 */ /* int64 */ /* native */ /* float */ /* managed p */ /* objref */
693 const static unsigned char
694 valid_unnops [TYPE_MAX] = {
695 TYPE_INVALID, TYPE_INT32, TYPE_INT64, TYPE_NINT, TYPE_FLOAT, TYPE_INVALID
696 /* int32 */ /* int64 */ /* native */ /* float */ /* managed p */ /* objref */
699 /* note: the resulting type is always a boolean */
700 const static unsigned char
701 valid_bincomp [TYPE_MAX] [TYPE_MAX] = {
703 {TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_INT32, TYPE_INVALID},
704 {TYPE_INVALID, TYPE_INVALID, TYPE_INT32, TYPE_INVALID},
705 {TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_INT32},
706 {TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INT32},
707 {TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_INT32},
708 {TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INVALID, TYPE_INT32}
709 /* int32 */ /* int64 */ /* native */ /* float */ /* managed p */ /* objref */
712 const static unsigned char
713 valid_intops [TYPE_MAX] [TYPE_MAX] = {
715 {TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_NINT},
716 {TYPE_INVALID, TYPE_INVALID, TYPE_INT64},
717 {TYPE_INVALID, TYPE_NINT, TYPE_INVALID, TYPE_NINT},
721 const static unsigned char
722 valid_shiftops [TYPE_MAX] [TYPE_MAX] = {
724 {TYPE_INVALID, TYPE_INT32, TYPE_INVALID, TYPE_INT32},
725 {TYPE_INVALID, TYPE_INT64, TYPE_INVALID, TYPE_INT64},
726 {TYPE_INVALID, TYPE_NINT, TYPE_INVALID, TYPE_NINT},
730 #define ADD_INVALID(list,msg) \
732 MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \
733 vinfo->status = MONO_VERIFY_ERROR; \
734 vinfo->message = (msg); \
735 (list) = g_slist_prepend ((list), vinfo); \
740 #define CHECK_STACK_UNDERFLOW(num) \
742 if (cur_stack < (num)) \
743 ADD_INVALID (list, g_strdup_printf ("Stack underflow at 0x%04x", ip_offset)); \
746 #define CHECK_STACK_OVERFLOW() \
748 if (cur_stack >= max_stack) \
749 ADD_INVALID (list, g_strdup_printf ("Maxstack exceeded at 0x%04x", ip_offset)); \
753 PREFIX_UNALIGNED = 1,
756 PREFIX_ADDR_MASK = 3,
776 in_any_block (MonoMethodHeader *header, guint offset)
779 MonoExceptionClause *clause;
781 for (i = 0; i < header->num_clauses; ++i) {
782 clause = &header->clauses [i];
783 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
785 if (MONO_OFFSET_IN_HANDLER (clause, offset))
787 /* need to check filter ... */
793 in_same_block (MonoMethodHeader *header, guint offset, guint target)
796 MonoExceptionClause *clause;
798 for (i = 0; i < header->num_clauses; ++i) {
799 clause = &header->clauses [i];
800 if (MONO_OFFSET_IN_CLAUSE (clause, offset) && !MONO_OFFSET_IN_CLAUSE (clause, target))
802 if (MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target))
804 /* need to check filter ... */
810 * A leave can't escape a finally block
813 is_correct_leave (MonoMethodHeader *header, guint offset, guint target)
816 MonoExceptionClause *clause;
818 for (i = 0; i < header->num_clauses; ++i) {
819 clause = &header->clauses [i];
820 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY && MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target))
822 /* need to check filter ... */
828 can_merge_stack (ILCodeDesc *a, ILCodeDesc *b)
830 if (!b->flags & CODE_SEEN) {
831 b->flags |= CODE_SEEN;
832 b->stack_count = a->stack_count;
836 if (a->stack_count != b->stack_count)
843 * FIXME: need to distinguish between valid and verifiable.
844 * Need to keep track of types on the stack.
845 * Verify types for opcodes.
848 mono_method_verify (MonoMethod *method, int level)
850 MonoMethodHeader *header;
851 MonoMethodSignature *signature, *csig;
854 register const unsigned char *ip;
855 register const unsigned char *end;
856 const unsigned char *target; /* branch target */
857 int max_args, max_stack, cur_stack, i, n, need_merge, start;
858 guint32 token, ip_offset;
859 char *local_state = NULL;
864 signature = method->signature;
865 header = ((MonoMethodNormal *)method)->header;
867 end = ip + header->code_size;
868 max_args = method->signature->param_count + method->signature->hasthis;
869 max_stack = header->max_stack;
870 need_merge = cur_stack = 0;
872 image = method->klass->image;
873 code = g_new0 (ILCodeDesc, header->code_size);
875 if (header->num_locals) {
876 local_state = g_new (char, header->num_locals);
877 memset (local_state, header->init_locals, header->num_locals);
879 g_print ("Method %s.%s::%s\n", method->klass->name_space, method->klass->name, method->name);
882 ip_offset = ip - header->code;
883 g_print ("IL_%04x: %02x\n", ip_offset, *ip);
884 if (start || !(code [ip_offset].flags & CODE_SEEN)) {
886 code [ip_offset].stack_count = 0;
889 code [ip_offset].stack_count = cur_stack;
891 code [ip_offset].flags |= CODE_SEEN;
894 if (code [ip_offset].stack_count != cur_stack)
895 ADD_INVALID (list, g_strdup_printf ("Cannot merge stack states at 0x%04x", ip_offset));
898 if (!can_merge_stack (&code [ip_offset], &code [target - header->code]))
899 ADD_INVALID (list, g_strdup_printf ("Cannot merge stack states at 0x%04x", ip_offset));
912 if (*ip - CEE_LDARG_0 >= max_args)
913 ADD_INVALID (list, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", *ip - CEE_LDARG_0, ip_offset));
914 CHECK_STACK_OVERFLOW ();
922 if (*ip - CEE_LDLOC_0 >= header->num_locals)
923 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", *ip - CEE_LDLOC_0, ip_offset));
924 if (!local_state [*ip - CEE_LDLOC_0])
925 ADD_INVALID (list, g_strdup_printf ("Local var %d is initialized at 0x%04x", *ip - CEE_LDLOC_0, ip_offset));
926 CHECK_STACK_OVERFLOW ();
934 if (*ip - CEE_STLOC_0 >= header->num_locals)
935 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", *ip - CEE_STLOC_0, ip_offset));
936 local_state [*ip - CEE_STLOC_0] = 1;
937 CHECK_STACK_UNDERFLOW (1);
943 if (ip [1] >= max_args)
944 ADD_INVALID (list, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", ip [1], ip_offset));
945 CHECK_STACK_OVERFLOW ();
950 if (ip [1] >= max_args)
951 ADD_INVALID (list, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", ip [1], ip_offset));
952 CHECK_STACK_UNDERFLOW (1);
958 if (ip [1] >= header->num_locals)
959 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", ip [1], ip_offset));
960 /* no need to check if the var is initialized if the address is taken */
961 if (*ip == CEE_LDLOC_S && !local_state [ip [1]])
962 ADD_INVALID (list, g_strdup_printf ("Local var %d is initialized at 0x%04x", ip [1], ip_offset));
963 CHECK_STACK_OVERFLOW ();
968 if (ip [1] >= header->num_locals)
969 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", ip [1], ip_offset));
970 local_state [ip [1]] = 1;
971 CHECK_STACK_UNDERFLOW (1);
976 CHECK_STACK_OVERFLOW ();
990 CHECK_STACK_OVERFLOW ();
995 CHECK_STACK_OVERFLOW ();
1000 CHECK_STACK_OVERFLOW ();
1005 CHECK_STACK_OVERFLOW ();
1010 CHECK_STACK_OVERFLOW ();
1015 CHECK_STACK_OVERFLOW ();
1019 case CEE_UNUSED99: ++ip; break; /* warn/error instead? */
1021 CHECK_STACK_UNDERFLOW (1);
1022 CHECK_STACK_OVERFLOW ();
1027 CHECK_STACK_UNDERFLOW (1);
1033 ADD_INVALID (list, g_strdup_printf ("Eval stack must be empty in jmp at 0x%04x", ip_offset));
1034 token = read32 (ip + 1);
1035 if (in_any_block (header, ip_offset))
1036 ADD_INVALID (list, g_strdup_printf ("jmp cannot escape exception blocks at 0x%04x", ip_offset));
1038 * FIXME: check signature, retval, arguments etc.
1044 token = read32 (ip + 1);
1046 * FIXME: we could just load the signature ...
1048 cmethod = mono_get_method (image, token, NULL);
1050 ADD_INVALID (list, g_strdup_printf ("Method 0x%08x not found at 0x%04x", token, ip_offset));
1051 csig = cmethod->signature;
1052 CHECK_STACK_UNDERFLOW (csig->param_count + csig->hasthis);
1053 cur_stack -= csig->param_count + csig->hasthis;
1054 if (csig->ret->type != MONO_TYPE_VOID) {
1055 CHECK_STACK_OVERFLOW ();
1061 token = read32 (ip + 1);
1063 * FIXME: check signature, retval, arguments etc.
1068 if (signature->ret->type != MONO_TYPE_VOID) {
1070 ADD_INVALID (list, g_strdup_printf ("Stack not empty after ret at 0x%04x", ip_offset));
1074 ADD_INVALID (list, g_strdup_printf ("Stack not empty after ret at 0x%04x", ip_offset));
1077 if (in_any_block (header, ip_offset))
1078 ADD_INVALID (list, g_strdup_printf ("ret cannot escape exception blocks at 0x%04x", ip_offset));
1082 target = ip + (signed char)ip [1] + 2;
1083 if (target >= end || target < header->code)
1084 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1085 if (!in_same_block (header, ip_offset, target - header->code))
1086 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1092 target = ip + (signed char)ip [1] + 2;
1093 if (target >= end || target < header->code)
1094 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1095 if (!in_same_block (header, ip_offset, target - header->code))
1096 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1097 CHECK_STACK_UNDERFLOW (1);
1112 target = ip + (signed char)ip [1] + 2;
1113 if (target >= end || target < header->code)
1114 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1115 if (!in_same_block (header, ip_offset, target - header->code))
1116 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1117 CHECK_STACK_UNDERFLOW (2);
1123 target = ip + (gint32)read32 (ip + 1) + 5;
1124 if (target >= end || target < header->code)
1125 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1126 if (!in_same_block (header, ip_offset, target - header->code))
1127 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1133 target = ip + (gint32)read32 (ip + 1) + 5;
1134 if (target >= end || target < header->code)
1135 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1136 if (!in_same_block (header, ip_offset, target - header->code))
1137 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1138 CHECK_STACK_UNDERFLOW (1);
1153 target = ip + (gint32)read32 (ip + 1) + 5;
1154 if (target >= end || target < header->code)
1155 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1156 if (!in_same_block (header, ip_offset, target - header->code))
1157 ADD_INVALID (list, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset));
1158 CHECK_STACK_UNDERFLOW (2);
1164 n = read32 (ip + 1);
1165 target = ip + sizeof (guint32) * n;
1166 /* FIXME: check that ip is in range (and within the same exception block) */
1167 for (i = 0; i < n; ++i)
1168 if (target + (gint32) read32 (ip + 5 + i * sizeof (gint32)) >= end || target + (gint32) read32 (ip + 5 + i * sizeof (gint32)) < header->code)
1169 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1170 CHECK_STACK_UNDERFLOW (1);
1172 ip += 5 + sizeof (guint32) * n;
1185 CHECK_STACK_UNDERFLOW (1);
1195 CHECK_STACK_UNDERFLOW (2);
1212 CHECK_STACK_UNDERFLOW (2);
1226 CHECK_STACK_UNDERFLOW (1);
1230 token = read32 (ip + 1);
1231 CHECK_STACK_UNDERFLOW (2);
1236 token = read32 (ip + 1);
1237 CHECK_STACK_UNDERFLOW (1);
1241 token = read32 (ip + 1);
1242 CHECK_STACK_OVERFLOW ();
1247 token = read32 (ip + 1);
1249 * FIXME: we could just load the signature ...
1251 cmethod = mono_get_method (image, token, NULL);
1253 ADD_INVALID (list, g_strdup_printf ("Constructor 0x%08x not found at 0x%04x", token, ip_offset));
1254 csig = cmethod->signature;
1255 CHECK_STACK_UNDERFLOW (csig->param_count);
1256 cur_stack -= csig->param_count;
1257 CHECK_STACK_OVERFLOW ();
1263 token = read32 (ip + 1);
1264 CHECK_STACK_UNDERFLOW (1);
1268 CHECK_STACK_UNDERFLOW (1);
1273 ++ip; /* warn, error ? */
1276 token = read32 (ip + 1);
1277 CHECK_STACK_UNDERFLOW (1);
1281 CHECK_STACK_UNDERFLOW (1);
1286 CHECK_STACK_UNDERFLOW (1);
1287 token = read32 (ip + 1);
1291 CHECK_STACK_UNDERFLOW (1);
1292 token = read32 (ip + 1);
1296 CHECK_STACK_UNDERFLOW (2);
1298 token = read32 (ip + 1);
1302 CHECK_STACK_OVERFLOW ();
1303 token = read32 (ip + 1);
1308 CHECK_STACK_OVERFLOW ();
1309 token = read32 (ip + 1);
1314 CHECK_STACK_UNDERFLOW (1);
1316 token = read32 (ip + 1);
1320 CHECK_STACK_UNDERFLOW (2);
1322 token = read32 (ip + 1);
1325 case CEE_CONV_OVF_I1_UN:
1326 case CEE_CONV_OVF_I2_UN:
1327 case CEE_CONV_OVF_I4_UN:
1328 case CEE_CONV_OVF_I8_UN:
1329 case CEE_CONV_OVF_U1_UN:
1330 case CEE_CONV_OVF_U2_UN:
1331 case CEE_CONV_OVF_U4_UN:
1332 case CEE_CONV_OVF_U8_UN:
1333 case CEE_CONV_OVF_I_UN:
1334 case CEE_CONV_OVF_U_UN:
1335 CHECK_STACK_UNDERFLOW (1);
1339 CHECK_STACK_UNDERFLOW (1);
1340 token = read32 (ip + 1);
1344 CHECK_STACK_UNDERFLOW (1);
1345 token = read32 (ip + 1);
1349 CHECK_STACK_UNDERFLOW (1);
1353 CHECK_STACK_UNDERFLOW (2);
1355 token = read32 (ip + 1);
1368 case CEE_LDELEM_REF:
1369 CHECK_STACK_UNDERFLOW (2);
1380 case CEE_STELEM_REF:
1381 CHECK_STACK_UNDERFLOW (3);
1401 ++ip; /* warn, error ? */
1403 case CEE_CONV_OVF_I1:
1404 case CEE_CONV_OVF_U1:
1405 case CEE_CONV_OVF_I2:
1406 case CEE_CONV_OVF_U2:
1407 case CEE_CONV_OVF_I4:
1408 case CEE_CONV_OVF_U4:
1409 case CEE_CONV_OVF_I8:
1410 case CEE_CONV_OVF_U8:
1411 CHECK_STACK_UNDERFLOW (1);
1421 ++ip; /* warn, error ? */
1424 CHECK_STACK_UNDERFLOW (1);
1428 CHECK_STACK_UNDERFLOW (1);
1433 ++ip; /* warn, error ? */
1436 CHECK_STACK_UNDERFLOW (1);
1437 token = read32 (ip + 1);
1449 ++ip; /* warn, error ? */
1452 CHECK_STACK_OVERFLOW ();
1453 token = read32 (ip + 1);
1460 case CEE_CONV_OVF_I:
1461 case CEE_CONV_OVF_U:
1462 CHECK_STACK_UNDERFLOW (1);
1466 case CEE_ADD_OVF_UN:
1468 case CEE_MUL_OVF_UN:
1470 case CEE_SUB_OVF_UN:
1471 CHECK_STACK_UNDERFLOW (2);
1475 case CEE_ENDFINALLY:
1480 target = ip + (gint32)read32(ip + 1) + 5;
1481 if (target >= end || target < header->code)
1482 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1483 if (!is_correct_leave (header, ip_offset, target - header->code))
1484 ADD_INVALID (list, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ip_offset));
1489 target = ip + (signed char)ip [1] + 2;
1490 if (target >= end || target < header->code)
1491 ADD_INVALID (list, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset));
1492 if (!is_correct_leave (header, ip_offset, target - header->code))
1493 ADD_INVALID (list, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ip_offset));
1498 CHECK_STACK_UNDERFLOW (2);
1503 CHECK_STACK_UNDERFLOW (1);
1544 CHECK_STACK_OVERFLOW ();
1552 CHECK_STACK_UNDERFLOW (2);
1557 CHECK_STACK_OVERFLOW ();
1558 token = read32 (ip + 1);
1562 CHECK_STACK_UNDERFLOW (1);
1563 token = read32 (ip + 1);
1571 if (read16 (ip + 1) >= max_args)
1572 ADD_INVALID (list, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", read16 (ip + 1), ip_offset));
1573 CHECK_STACK_OVERFLOW ();
1578 if (read16 (ip + 1) >= max_args)
1579 ADD_INVALID (list, g_strdup_printf ("Method doesn't have argument %d at 0x%04x", read16(ip + 1), ip_offset));
1580 CHECK_STACK_UNDERFLOW (1);
1586 if (read16 (ip + 1) >= header->num_locals)
1587 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", read16 (ip + 1), ip_offset));
1588 /* no need to check if the var is initialized if the address is taken */
1589 if (*ip == CEE_LDLOC && !local_state [read16 (ip + 1)])
1590 ADD_INVALID (list, g_strdup_printf ("Local var %d is initialized at 0x%04x", read16 (ip + 1), ip_offset));
1591 CHECK_STACK_OVERFLOW ();
1596 if (read16 (ip + 1) >= header->num_locals)
1597 ADD_INVALID (list, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", read16 (ip + 1), ip_offset));
1598 local_state [read16 (ip + 1)] = 1;
1599 CHECK_STACK_UNDERFLOW (1);
1605 ADD_INVALID (list, g_strdup_printf ("Stack must have only size item in localloc at 0x%04x", ip_offset));
1613 ADD_INVALID (list, g_strdup_printf ("Stack must have only filter result in endfilter at 0x%04x", ip_offset));
1616 case CEE_UNALIGNED_:
1617 prefix |= PREFIX_UNALIGNED;
1621 prefix |= PREFIX_VOLATILE;
1625 prefix |= PREFIX_TAIL;
1627 if (ip < end && (*ip != CEE_CALL && *ip != CEE_CALLI && *ip != CEE_CALLVIRT))
1628 ADD_INVALID (list, g_strdup_printf ("tail prefix must be used only with call opcodes at 0x%04x", ip_offset));
1631 CHECK_STACK_UNDERFLOW (1);
1632 token = read32 (ip + 1);
1639 CHECK_STACK_UNDERFLOW (3);
1643 CHECK_STACK_UNDERFLOW (3);
1656 CHECK_STACK_OVERFLOW ();
1657 token = read32 (ip + 1);
1660 case CEE_REFANYTYPE:
1661 CHECK_STACK_UNDERFLOW (1);
1675 * FIXME: if ip != end we overflowed: mark as error.
1679 g_free (local_state);