Tue Sep 11 16:08:08 CEST 2007 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / image.c
index ecd8a81f5083af8e640f5512c3c183c8b6c76375..a90ddb0033e26b7bb49d4dc420a95cceb5ac94c4 100644 (file)
@@ -642,43 +642,35 @@ mono_image_init (MonoImage *image)
        image->method_signatures = g_hash_table_new (NULL, NULL);
 }
 
-static MonoImage *
-do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
-                   gboolean care_about_cli)
-{
-       MonoCLIImageInfo *iinfo;
-       MonoDotNetHeader *header;
-       MonoMSDOSHeader msdos;
-       guint32 offset = 0;
-
-       mono_image_init (image);
-
-       iinfo = image->image_info;
-       header = &iinfo->cli_header;
-               
-       if (status)
-               *status = MONO_IMAGE_IMAGE_INVALID;
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+#define SWAP64(x) (x) = GUINT64_FROM_LE ((x))
+#define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
+#define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
+#define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
+#else
+#define SWAP64(x)
+#define SWAP32(x)
+#define SWAP16(x)
+#define SWAPPDE(x)
+#endif
 
-       if (offset + sizeof (msdos) > image->raw_data_len)
-               goto invalid_image;
-       memcpy (&msdos, image->raw_data + offset, sizeof (msdos));
-       
-       if (!(msdos.msdos_sig [0] == 'M' && msdos.msdos_sig [1] == 'Z'))
-               goto invalid_image;
-       
-       msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset);
+/*
+ * Returns < 0 to indicate an error.
+ */
+static int
+do_load_header (MonoImage *image, MonoDotNetHeader *header, int offset)
+{
+       MonoDotNetHeader64 header64;
 
-       offset = msdos.pe_offset;
+       if (offset + sizeof (MonoDotNetHeader32) > image->raw_data_len)
+               return -1;
 
-       if (offset + sizeof (MonoDotNetHeader) > image->raw_data_len)
-               goto invalid_image;
        memcpy (header, image->raw_data + offset, sizeof (MonoDotNetHeader));
-       offset += sizeof (MonoDotNetHeader);
 
-#if G_BYTE_ORDER != G_LITTLE_ENDIAN
-#define SWAP32(x) (x) = GUINT32_FROM_LE ((x))
-#define SWAP16(x) (x) = GUINT16_FROM_LE ((x))
-#define SWAPPDE(x) do { (x).rva = GUINT32_FROM_LE ((x).rva); (x).size = GUINT32_FROM_LE ((x).size);} while (0)
+       if (header->pesig [0] != 'P' || header->pesig [1] != 'E')
+               return -1;
+
+       /* endian swap the fields common between PE and PE+ */
        SWAP32 (header->coff.coff_time);
        SWAP32 (header->coff.coff_symptr);
        SWAP32 (header->coff.coff_symcount);
@@ -688,15 +680,71 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
        SWAP16 (header->coff.coff_attributes);
        /* MonoPEHeader */
        SWAP32 (header->pe.pe_code_size);
-       SWAP32 (header->pe.pe_data_size);
        SWAP32 (header->pe.pe_uninit_data_size);
        SWAP32 (header->pe.pe_rva_entry_point);
        SWAP32 (header->pe.pe_rva_code_base);
        SWAP32 (header->pe.pe_rva_data_base);
        SWAP16 (header->pe.pe_magic);
 
+       /* now we are ready for the basic tests */
+
+       if (header->pe.pe_magic == 0x10B) {
+               offset += sizeof (MonoDotNetHeader);
+               SWAP32 (header->pe.pe_data_size);
+               if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4))
+                       return -1;
+
+               SWAP32  (header->nt.pe_image_base);     /* must be 0x400000 */
+               SWAP32  (header->nt.pe_stack_reserve);
+               SWAP32  (header->nt.pe_stack_commit);
+               SWAP32  (header->nt.pe_heap_reserve);
+               SWAP32  (header->nt.pe_heap_commit);
+       } else if (header->pe.pe_magic == 0x20B) {
+               /* PE32+ file format */
+               if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader64) - sizeof (MonoCOFFHeader) - 4))
+                       return -1;
+               memcpy (&header64, image->raw_data + offset, sizeof (MonoDotNetHeader64));
+               offset += sizeof (MonoDotNetHeader64);
+               /* copy the fields already swapped. the last field, pe_data_size, is missing */
+               memcpy (&header64, header, sizeof (MonoDotNetHeader) - 4);
+               /* FIXME: we lose bits here, but we don't use this stuff internally, so we don't care much.
+                * will be fixed when we change MonoDotNetHeader to not match the 32 bit variant
+                */
+               SWAP64  (header64.nt.pe_image_base);
+               header->nt.pe_image_base = header64.nt.pe_image_base;
+               SWAP64  (header64.nt.pe_stack_reserve);
+               header->nt.pe_stack_reserve = header64.nt.pe_stack_reserve;
+               SWAP64  (header64.nt.pe_stack_commit);
+               header->nt.pe_stack_commit = header64.nt.pe_stack_commit;
+               SWAP64  (header64.nt.pe_heap_reserve);
+               header->nt.pe_heap_reserve = header64.nt.pe_heap_reserve;
+               SWAP64  (header64.nt.pe_heap_commit);
+               header->nt.pe_heap_commit = header64.nt.pe_heap_commit;
+
+               header->nt.pe_section_align = header64.nt.pe_section_align;
+               header->nt.pe_file_alignment = header64.nt.pe_file_alignment;
+               header->nt.pe_os_major = header64.nt.pe_os_major;
+               header->nt.pe_os_minor = header64.nt.pe_os_minor;
+               header->nt.pe_user_major = header64.nt.pe_user_major;
+               header->nt.pe_user_minor = header64.nt.pe_user_minor;
+               header->nt.pe_subsys_major = header64.nt.pe_subsys_major;
+               header->nt.pe_subsys_minor = header64.nt.pe_subsys_minor;
+               header->nt.pe_reserved_1 = header64.nt.pe_reserved_1;
+               header->nt.pe_image_size = header64.nt.pe_image_size;
+               header->nt.pe_header_size = header64.nt.pe_header_size;
+               header->nt.pe_checksum = header64.nt.pe_checksum;
+               header->nt.pe_subsys_required = header64.nt.pe_subsys_required;
+               header->nt.pe_dll_flags = header64.nt.pe_dll_flags;
+               header->nt.pe_loader_flags = header64.nt.pe_loader_flags;
+               header->nt.pe_data_dir_count = header64.nt.pe_data_dir_count;
+
+               /* copy the datadir */
+               memcpy (&header->datadir, &header64.datadir, sizeof (MonoPEDatadir));
+       } else {
+               return -1;
+       }
+
        /* MonoPEHeaderNT: not used yet */
-       SWAP32  (header->nt.pe_image_base);     /* must be 0x400000 */
        SWAP32  (header->nt.pe_section_align);       /* must be 8192 */
        SWAP32  (header->nt.pe_file_alignment);      /* must be 512 or 4096 */
        SWAP16  (header->nt.pe_os_major);            /* must be 4 */
@@ -711,10 +759,6 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
        SWAP32  (header->nt.pe_checksum);
        SWAP16  (header->nt.pe_subsys_required);
        SWAP16  (header->nt.pe_dll_flags);
-       SWAP32  (header->nt.pe_stack_reserve);
-       SWAP32  (header->nt.pe_stack_commit);
-       SWAP32  (header->nt.pe_heap_reserve);
-       SWAP32  (header->nt.pe_heap_commit);
        SWAP32  (header->nt.pe_loader_flags);
        SWAP32  (header->nt.pe_data_dir_count);
 
@@ -736,19 +780,47 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
        SWAPPDE (header->datadir.pe_cli_header);
        SWAPPDE (header->datadir.pe_reserved);
 
-#undef SWAP32
-#undef SWAP16
-#undef SWAPPDE
-#endif
+       return offset;
+}
 
-       if (header->coff.coff_machine != 0x14c)
+static MonoImage *
+do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
+                   gboolean care_about_cli)
+{
+       MonoCLIImageInfo *iinfo;
+       MonoDotNetHeader *header;
+       MonoMSDOSHeader msdos;
+       gint32 offset = 0;
+
+       mono_image_init (image);
+
+       iinfo = image->image_info;
+       header = &iinfo->cli_header;
+               
+       if (status)
+               *status = MONO_IMAGE_IMAGE_INVALID;
+
+       if (offset + sizeof (msdos) > image->raw_data_len)
                goto invalid_image;
+       memcpy (&msdos, image->raw_data + offset, sizeof (msdos));
+       
+       if (!(msdos.msdos_sig [0] == 'M' && msdos.msdos_sig [1] == 'Z'))
+               goto invalid_image;
+       
+       msdos.pe_offset = GUINT32_FROM_LE (msdos.pe_offset);
 
-       if (header->coff.coff_opt_header_size != (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4))
+       offset = msdos.pe_offset;
+
+       offset = do_load_header (image, header, offset);
+       if (offset < 0)
                goto invalid_image;
 
-       if (header->pesig[0] != 'P' || header->pesig[1] != 'E' || header->pe.pe_magic != 0x10B)
+       /*
+        * this tests for a x86 machine type, but itanium, amd64 and others could be used, too.
+        * we skip this test.
+       if (header->coff.coff_machine != 0x14c)
                goto invalid_image;
+       */
 
 #if 0
        /*
@@ -1529,8 +1601,11 @@ mono_image_load_file_for_image (MonoImage *image, int fileidx)
        if (fileidx < 1 || fileidx > t->rows)
                return NULL;
 
-       if (image->files && image->files [fileidx - 1])
+       mono_loader_lock ();
+       if (image->files && image->files [fileidx - 1]) {
+               mono_loader_unlock ();
                return image->files [fileidx - 1];
+       }
 
        if (!image->files)
                image->files = g_new0 (MonoImage*, t->rows);
@@ -1551,6 +1626,7 @@ mono_image_load_file_for_image (MonoImage *image, int fileidx)
 
                image->files [fileidx - 1] = res;
        }
+       mono_loader_unlock ();
        g_free (name);
        g_free (base_dir);
        return res;