2007-11-15 Dick Porter <dick@ximian.com>
authorDick Porter <dick@acm.org>
Thu, 15 Nov 2007 17:13:51 +0000 (17:13 -0000)
committerDick Porter <dick@acm.org>
Thu, 15 Nov 2007 17:13:51 +0000 (17:13 -0000)
* process.c: Read file version info from the files pointed at by
process modules, not the current process.  Fixes bug 315969.

Use windows typedef names in some places to fix warnings on the
windows build.

2007-11-15  Dick Porter  <dick@ximian.com>

* processes.c: Implement process module support by reading from
/proc if it's available.

* versioninfo.h:
* versioninfo.c: New functions to implement PE file version info
support.

svn path=/trunk/mono/; revision=89688

mono/io-layer/ChangeLog
mono/io-layer/Makefile.am
mono/io-layer/processes.c
mono/io-layer/processes.h
mono/io-layer/uglify.h
mono/io-layer/versioninfo.c [new file with mode: 0644]
mono/io-layer/versioninfo.h
mono/metadata/ChangeLog
mono/metadata/icall-def.h
mono/metadata/process.c
mono/metadata/process.h

index f3dfee19f4b7b2a4d1fb926e53a850fd89469531..5feafad47b82846bf105abe126ee608c4bd76dab 100644 (file)
@@ -1,3 +1,12 @@
+2007-11-15  Dick Porter  <dick@ximian.com>
+
+       * processes.c: Implement process module support by reading from
+       /proc if it's available.
+
+       * versioninfo.h: 
+       * versioninfo.c: New functions to implement PE file version info
+       support.
+
 2007-11-08  Dick Porter  <dick@ximian.com>
 
        * critical-sections.h (EnterCriticalSection): Disable the critical
index a0411df644946cc4ecd639c125296d297155b54c..3da801a216f88379c65ff5988eb38a5dffcd0171 100644 (file)
@@ -96,6 +96,7 @@ OTHER_SRC = \
        timefuncs-private.h     \
        types.h                 \
        uglify.h                \
+       versioninfo.c           \
        versioninfo.h           \
        wait.c                  \
        wait.h                  \
index cd6176cdda244be38ee0ca724979ae09b5a604f2..61987706498549f5fe6961f953620497f2e38afa 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <config.h>
 #include <glib.h>
+#include <stdio.h>
 #include <string.h>
 #include <pthread.h>
 #include <sched.h>
@@ -21,6 +22,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <fcntl.h>
+#include <sys/param.h>
 
 /* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
 #ifdef __APPLE__
@@ -1533,9 +1535,162 @@ gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
        return(TRUE);
 }
 
+typedef struct
+{
+       gpointer address_start;
+       gpointer address_end;
+       gchar *perms;
+       gpointer address_offset;
+       dev_t device;
+       ino_t inode;
+       gchar *filename;
+} WapiProcModule;
+
+static void free_procmodule (WapiProcModule *mod)
+{
+       if (mod->perms != NULL) {
+               g_free (mod->perms);
+       }
+       if (mod->filename != NULL) {
+               g_free (mod->filename);
+       }
+       g_free (mod);
+}
+
+static GSList *load_modules (FILE *fp)
+{
+       gchar buf[MAXPATHLEN + 1], **fields, **addresses;
+       GSList *ret = NULL;
+       WapiProcModule *mod;
+       char *ep;
+       long address_start, address_end, address_offset, maj, min;
+       dev_t device;
+       ino_t inode;
+       
+       while (fgets (buf, sizeof(buf), fp)) {
+               /* Have to specify 7 fields here, as otherwise
+                * g_strsplit_set gives us variable numbers of empty
+                * fields when the input buffer contains contiguous
+                * spaces before the filename.  Unuseful.
+                */
+               fields = g_strsplit_set (buf, " \t:", 7);
+               if (g_strv_length (fields) < 7) {
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               g_strstrip (fields[6]);
+               if ((strlen (fields[0]) == 0) ||
+                   (strlen (fields[2]) == 0) ||
+                   (strlen (fields[3]) == 0) ||
+                   (strlen (fields[4]) == 0) ||
+                   (strlen (fields[5]) == 0) ||
+                   (strlen (fields[6]) == 0)) {
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               ep = NULL;
+               address_offset = strtol (fields[2], &ep, 16);
+               if ((address_offset == LONG_MIN) ||
+                   (address_offset == LONG_MAX) ||
+                   (ep == NULL) ||
+                   (*ep != '\0')) {
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               ep = NULL;
+               maj = strtol (fields[3], &ep, 16);
+               if ((maj == LONG_MIN) ||
+                   (maj == LONG_MAX) ||
+                   (ep == NULL) ||
+                   (*ep != '\0')) {
+                       g_strfreev (fields);
+                       continue;
+               }
+               
+               ep = NULL;
+               min = strtol (fields[4], &ep, 16);
+               if ((min == LONG_MIN) ||
+                   (min == LONG_MAX) ||
+                   (ep == NULL) ||
+                   (*ep != '\0')) {
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               inode = (ino_t)atoi (fields[5]);
+               device = makedev ((int)maj, (int)min);
+               
+               if ((device == 0) &&
+                   (inode == 0)) {
+                       g_strfreev (fields);
+                       continue;
+               }
+                       
+               addresses = g_strsplit (fields[0], "-", -1);
+               if (g_strv_length (addresses) != 2) {
+                       g_strfreev (addresses);
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               ep = NULL;
+               address_start = strtol (addresses[0], &ep, 16);
+               if ((address_start == LONG_MIN) ||
+                   (address_start == LONG_MAX) ||
+                   (ep == NULL) ||
+                   (*ep != '\0')) {
+                       g_strfreev (addresses);
+                       g_strfreev (fields);
+                       continue;
+               }
+
+               ep = NULL;
+               address_end = strtol (addresses[1], &ep, 16);
+               if ((address_end == LONG_MIN) ||
+                   (address_end == LONG_MAX) ||
+                   (ep == NULL) ||
+                   (*ep != '\0')) {
+                       g_strfreev (addresses);
+                       g_strfreev (fields);
+                       continue;
+               }
+               
+               
+               mod = g_new0 (WapiProcModule, 1);
+               mod->address_start = GUINT_TO_POINTER (address_start);
+               mod->address_end = GUINT_TO_POINTER (address_end);
+               mod->perms = NULL;
+               mod->address_offset = GUINT_TO_POINTER (address_offset);
+               mod->device = device;
+               mod->inode = inode;
+               mod->filename = g_strdup (fields[6]);
+               
+               ret = g_slist_prepend (ret, mod);
+               
+               g_strfreev (addresses);
+               g_strfreev (fields);
+       }
+
+       ret = g_slist_reverse (ret);
+       
+       return(ret);
+}
+
 gboolean EnumProcessModules (gpointer process, gpointer *modules,
                             guint32 size, guint32 *needed)
 {
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *module;
+       guint32 count, avail = size / sizeof(gpointer);
+       int i;
+       
        /* Store modules in an array of pointers (main module as
         * modules[0]), using the load address for each module as a
         * token.  (Use 'NULL' as an alternative for the main module
@@ -1544,26 +1699,72 @@ gboolean EnumProcessModules (gpointer process, gpointer *modules,
         * other systems will have to implement /dev/kmem reading or
         * whatever other horrid technique is needed.
         */
-       if(size<sizeof(gpointer)) {
+       if (size < sizeof(gpointer)) {
                return(FALSE);
        }
-       
-#ifdef linux
-       modules[0]=NULL;
-       *needed=sizeof(gpointer);
-#else
-       modules[0]=NULL;
-       *needed=sizeof(gpointer);
+
+       ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                 (gpointer *)&process_handle);
+       if (ok == FALSE) {
+#ifdef DEBUG
+               g_message ("%s: Can't find process %p", __func__, process);
 #endif
+               
+               return(FALSE);
+       }
+       
+       filename = g_strdup_printf ("/proc/%d/maps", process_handle->id);
+       if ((fp = fopen (filename, "r")) == NULL) {
+               /* No /proc/<pid>/maps so just return the main module
+                * shortcut for now
+                */
+               modules[0] = NULL;
+               *needed = sizeof(gpointer);
+       } else {
+               mods = load_modules (fp);
+               count = g_slist_length (mods);
+               
+               *needed = sizeof(gpointer) * count;
+
+               /* Use the NULL shortcut, as the first line in
+                * /proc/<pid>/maps isn't the executable, and we need
+                * that first in the returned list
+                */
+               modules[0] = NULL;
+               for (i = 1; i < avail && i < count; i++) {
+                       module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                       modules[i] = module->address_start;
+               }
+               
+               for (i = 0; i < count; i++) {
+                       free_procmodule (g_slist_nth_data (mods, i));
+               }
+               g_slist_free (mods);
+       }
+
+       fclose (fp);
+       g_free (filename);
        
        return(TRUE);
 }
 
-guint32 GetModuleBaseName (gpointer process, gpointer module,
-                          gunichar2 *basename, guint32 size)
+static guint32 get_module_name (gpointer process, gpointer module,
+                               gunichar2 *basename, guint32 size,
+                               gboolean base)
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
+       pid_t pid;
+       gunichar2 *procname;
+       gchar *procname_ext = NULL;
+       glong len;
+       gsize bytes;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *found_module;
+       guint32 count;
+       int i;
        
        mono_once (&process_current_once, process_set_current);
 
@@ -1572,50 +1773,85 @@ guint32 GetModuleBaseName (gpointer process, gpointer module,
                   __func__, process, module);
 #endif
 
-       if(basename==NULL || size==0) {
-               return(FALSE);
+       if (basename == NULL || size == 0) {
+               return(0);
        }
        
-       ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
-                               (gpointer *)&process_handle);
-       if(ok==FALSE) {
+       ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                 (gpointer *)&process_handle);
+       if (ok == FALSE) {
 #ifdef DEBUG
                g_message ("%s: Can't find process %p", __func__, process);
 #endif
                
-               return(FALSE);
+               return(0);
        }
+       pid = process_handle->id;
 
-       if(module==NULL) {
+       if (module == NULL) {
                /* Shorthand for the main module, which has the
                 * process name recorded in the handle data
                 */
-               pid_t pid;
-               gunichar2 *procname;
-               gchar *procname_utf8 = NULL;
-               glong len, bytes;
                
 #ifdef DEBUG
                g_message ("%s: Returning main module name", __func__);
 #endif
 
-               pid=process_handle->id;
-               procname_utf8 = process_handle->proc_name;
-       
+               if (base) {
+                       procname_ext = g_path_get_basename (process_handle->proc_name);
+               } else {
+                       procname_ext = g_strdup (process_handle->proc_name);
+               }
+       } else {
+               /* Look up the address in /proc/<pid>/maps */
+               filename = g_strdup_printf ("/proc/%d/maps", pid);
+               if ((fp = fopen (filename, "r")) == NULL) {
+                       /* No /proc/<pid>/maps, so just return failure
+                        * for now
+                        */
+                       g_free (filename);
+                       return(0);
+               } else {
+                       mods = load_modules (fp);
+                       fclose (fp);
+                       count = g_slist_length (mods);
+                       
+                       for (i = 0; i < count; i++) {
+                               found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                               if (procname_ext == NULL &&
+                                   found_module->address_start == module) {
+                                       if (base) {
+                                               procname_ext = g_path_get_basename (found_module->filename);
+                                       } else {
+                                               procname_ext = g_strdup (found_module->filename);
+                                       }
+                               }
+
+                               free_procmodule (found_module);
+                       }
+
+                       g_slist_free (mods);
+                       g_free (filename);
+               }
+       }
+
+       if (procname_ext != NULL) {
 #ifdef DEBUG
                g_message ("%s: Process name is [%s]", __func__,
-                          procname_utf8);
+                          procname_ext);
 #endif
 
-               procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
-                                           NULL);
+               procname = mono_unicode_from_external (procname_ext, &bytes);
                if (procname == NULL) {
                        /* bugger */
+                       g_free (procname_ext);
                        return(0);
                }
-
-               /* Add the terminator, and convert chars to bytes */
-               bytes = (len + 1) * 2;
+               
+               len = (bytes / 2);
+               
+               /* Add the terminator */
+               bytes += 2;
                
                if (size < bytes) {
 #ifdef DEBUG
@@ -1633,13 +1869,102 @@ guint32 GetModuleBaseName (gpointer process, gpointer module,
                }
                
                g_free (procname);
-
+               g_free (procname_ext);
+               
                return(len);
+       }
+       
+       return(0);
+}
+
+guint32 GetModuleBaseName (gpointer process, gpointer module,
+                          gunichar2 *basename, guint32 size)
+{
+       return(get_module_name (process, module, basename, size, TRUE));
+}
+
+guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+                            gunichar2 *filename, guint32 size)
+{
+       return(get_module_name (process, module, filename, size, FALSE));
+}
+
+gboolean GetModuleInformation (gpointer process, gpointer module,
+                              WapiModuleInfo *modinfo, guint32 size)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       pid_t pid;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *found_module;
+       guint32 count;
+       int i;
+       gboolean ret = FALSE;
+       
+       mono_once (&process_current_once, process_set_current);
+       
+#ifdef DEBUG
+       g_message ("%s: Getting module info, process handle %p module %p",
+                  __func__, process, module);
+#endif
+
+       if (modinfo == NULL || size < sizeof(WapiModuleInfo)) {
+               return(FALSE);
+       }
+       
+       ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                 (gpointer *)&process_handle);
+       if (ok == FALSE) {
+#ifdef DEBUG
+               g_message ("%s: Can't find process %p", __func__, process);
+#endif
+
+               return(FALSE);
+       }
+       pid = process_handle->id;
+       
+       if (module == NULL) {
+               /* Shorthand for the main module, which has the
+                * process name recorded in the handle data
+                *
+                * FIXME: try and dig through the /proc/<pid>/maps
+                * list matching filename?
+                */
+               return(FALSE);
        } else {
                /* Look up the address in /proc/<pid>/maps */
+               filename = g_strdup_printf ("/proc/%d/maps", pid);
+               if ((fp = fopen (filename, "r")) == NULL) {
+                       /* No /proc/<pid>/maps, so just return failure
+                        * for now
+                        */
+                       g_free (filename);
+                       return(FALSE);
+               } else {
+                       mods = load_modules (fp);
+                       fclose (fp);
+                       count = g_slist_length (mods);
+                       
+                       for (i = 0; i < count; i++) {
+                               found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                               if (found_module->address_start == module) {
+                                       modinfo->lpBaseOfDll = found_module->address_start;
+                                       modinfo->SizeOfImage = GPOINTER_TO_UINT(found_module->address_end) - GPOINTER_TO_UINT (found_module->address_start);
+                                       modinfo->EntryPoint = found_module->address_offset;
+                                       ret = TRUE;
+                               }
+                               
+                               free_procmodule (found_module);
+                       }
+                       
+                       g_slist_free (mods);
+                       g_free (filename);
+               }
        }
        
-       return(0);
+       return(ret);
 }
 
 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
index d00aa7efd206e780263e9551665eabe4e0f6b2cb..35122c424ee0e59a141ecfe14d3f22a361afa03d 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <mono/io-layer/handles.h>
 #include <mono/io-layer/access.h>
+#include <mono/io-layer/versioninfo.h>
 
 G_BEGIN_DECLS
 
@@ -199,6 +200,10 @@ extern gboolean EnumProcessModules (gpointer process, gpointer *modules,
                                    guint32 size, guint32 *needed);
 extern guint32 GetModuleBaseName (gpointer process, gpointer module,
                                  gunichar2 *basename, guint32 size);
+extern guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+                                   gunichar2 *filename, guint32 size);
+extern gboolean GetModuleInformation (gpointer process, gpointer module,
+                                     WapiModuleInfo *modinfo, guint32 size);
 extern gboolean GetProcessWorkingSetSize (gpointer process, size_t *min,
                                          size_t *max);
 extern gboolean SetProcessWorkingSetSize (gpointer process, size_t min,
index 1c51fcaa2c6e0f9694abb00713d33360abad6666..96e4838916b3dc866e674371b3b08fdd60c80dd7 100644 (file)
@@ -74,6 +74,34 @@ typedef WapiStartupInfo STARTUPINFO;
 typedef WapiStartupInfo *LPSTARTUPINFO;
 typedef WapiProcessInformation PROCESS_INFORMATION;
 typedef WapiFixedFileInfo VS_FIXEDFILEINFO;
+typedef WapiModuleInfo MODULEINFO;
+typedef WapiModuleInfo *LPMODULEINFO;
+typedef WapiImageDosHeader IMAGE_DOS_HEADER;
+typedef WapiImageDosHeader *PIMAGE_DOS_HEADER;
+typedef WapiImageFileHeader IMAGE_FILE_HEADER;
+typedef WapiImageFileHeader *PIMAGE_FILE_HEADER;
+typedef WapiImageDataDirectory IMAGE_DATA_DIRECTORY;
+typedef WapiImageDataDirectory *PIMAGE_DATA_DIRECTORY;
+typedef WapiImageOptionalHeader32 IMAGE_OPTIONAL_HEADER32;
+typedef WapiImageOptionalHeader32 *PIMAGE_OPTIONAL_HEADER32;
+typedef WapiImageOptionalHeader64 IMAGE_OPTIONAL_HEADER64;
+typedef WapiImageOptionalHeader64 *PIMAGE_OPTIONAL_HEADER64;
+typedef WapiImageOptionalHeader IMAGE_OPTIONAL_HEADER;
+typedef WapiImageOptionalHeader *PIMAGE_OPTIONAL_HEADER;
+typedef WapiImageNTHeaders32 IMAGE_NT_HEADERS32;
+typedef WapiImageNTHeaders32 *PIMAGE_NT_HEADERS32;
+typedef WapiImageNTHeaders64 IMAGE_NT_HEADERS64;
+typedef WapiImageNTHeaders64 *PIMAGE_NT_HEADERS64;
+typedef WapiImageNTHeaders IMAGE_NT_HEADERS;
+typedef WapiImageNTHeaders *PIMAGE_NT_HEADERS;
+typedef WapiImageSectionHeader IMAGE_SECTION_HEADER;
+typedef WapiImageSectionHeader *PIMAGE_SECTION_HEADER;
+typedef WapiImageResourceDirectory IMAGE_RESOURCE_DIRECTORY;
+typedef WapiImageResourceDirectory *PIMAGE_RESOURCE_DIRECTORY;
+typedef WapiImageResourceDirectoryEntry IMAGE_RESOURCE_DIRECTORY_ENTRY;
+typedef WapiImageResourceDirectoryEntry *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
+typedef WapiImageResourceDataEntry IMAGE_RESOURCE_DATA_ENTRY;
+typedef WapiImageResourceDataEntry *PIMAGE_RESOURCE_DATA_ENTRY;
 typedef WapiApcProc PAPCFUNC;
 typedef WapiShellExecuteInfo SHELLEXECUTEINFO;
 typedef WapiShellExecuteInfo *LPSHELLEXECUTEINFO;
diff --git a/mono/io-layer/versioninfo.c b/mono/io-layer/versioninfo.c
new file mode 100644 (file)
index 0000000..63145a8
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * versioninfo.c:  Version information support
+ *
+ * Author:
+ *     Dick Porter (dick@ximian.com)
+ *
+ * (C) 2007 Novell, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <mono/io-layer/wapi.h>
+#include <mono/io-layer/wapi-private.h>
+#include <mono/io-layer/versioninfo.h>
+#include <mono/io-layer/io-portability.h>
+#include <mono/io-layer/error.h>
+#include <mono/utils/strenc.h>
+
+#undef DEBUG
+
+static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiImageNTHeaders *nt_headers)
+{
+       WapiImageSectionHeader *section = IMAGE_FIRST_SECTION (nt_headers);
+       guint32 i;
+       
+       for (i = 0; i < nt_headers->FileHeader.NumberOfSections; i++, section++) {
+               guint32 size = section->Misc.VirtualSize;
+               if (size == 0) {
+                       size = section->SizeOfRawData;
+               }
+               
+               if ((rva >= section->VirtualAddress) &&
+                   (rva < (section->VirtualAddress + size))) {
+                       return(section);
+               }
+       }
+       
+       return(NULL);
+}
+
+static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders *ntheaders,
+                                 gpointer file_map)
+{
+       WapiImageSectionHeader *section_header;
+       guint32 delta;
+       
+       section_header = get_enclosing_section_header (rva, ntheaders);
+       if (section_header == NULL) {
+               return(NULL);
+       }
+       
+       delta = (guint32)(section_header->VirtualAddress -
+                         section_header->PointerToRawData);
+       
+       return(GUINT_TO_POINTER (GPOINTER_TO_UINT (file_map) + rva - delta));
+}
+
+static gpointer scan_resource_dir (WapiImageResourceDirectory *root,
+                                  WapiImageNTHeaders *nt_headers,
+                                  gpointer file_map,
+                                  WapiImageResourceDirectoryEntry *entry,
+                                  int level, guint32 res_id, guint32 lang_id,
+                                  guint32 *size)
+{
+       gboolean is_string = entry->NameIsString;
+       gboolean is_dir = entry->DataIsDirectory;
+       guint32 name_offset = GUINT32_FROM_LE (entry->NameOffset);
+       guint32 dir_offset = GUINT32_FROM_LE (entry->OffsetToDirectory);
+       guint32 data_offset = GUINT32_FROM_LE (entry->OffsetToData);
+       
+       if (level == 0) {
+               /* Normally holds a directory entry for each type of
+                * resource
+                */
+               if ((is_string == FALSE &&
+                    name_offset != res_id) ||
+                   (is_string == TRUE)) {
+                       return(NULL);
+               }
+       } else if (level == 1) {
+               /* Normally holds a directory entry for each resource
+                * item
+                */
+       } else if (level == 2) {
+               /* Normally holds a directory entry for each language
+                */
+               if ((is_string == FALSE &&
+                    name_offset != lang_id &&
+                    lang_id != 0) ||
+                   (is_string == TRUE)) {
+                       return(NULL);
+               }
+       } else {
+               g_assert_not_reached ();
+       }
+       
+       if (is_dir == TRUE) {
+               WapiImageResourceDirectory *res_dir = (WapiImageResourceDirectory *)GUINT_TO_POINTER (GPOINTER_TO_UINT (root) + dir_offset);
+               WapiImageResourceDirectoryEntry *sub_entries = (WapiImageResourceDirectoryEntry *)(res_dir + 1);
+               guint32 entries, i;
+               
+               entries = GUINT16_FROM_LE (res_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (res_dir->NumberOfIdEntries);
+               
+               for (i = 0; i < entries; i++) {
+                       WapiImageResourceDirectoryEntry *sub_entry = &sub_entries[i];
+                       gpointer ret;
+                       
+                       ret = scan_resource_dir (root, nt_headers, file_map,
+                                                sub_entry, level + 1, res_id,
+                                                lang_id, size);
+                       if (ret != NULL) {
+                               return(ret);
+                       }
+               }
+               
+               return(NULL);
+       } else {
+               WapiImageResourceDataEntry *data_entry = (WapiImageResourceDataEntry *)GUINT_TO_POINTER (GPOINTER_TO_UINT (root) + data_offset);
+               *size = GUINT32_FROM_LE (data_entry->Size);
+               
+               return(get_ptr_from_rva (data_entry->OffsetToData, nt_headers, file_map));
+       }
+}
+
+static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size,
+                                       guint32 res_id, guint32 lang_id,
+                                       guint32 *size)
+{
+       WapiImageDosHeader *dos_header;
+       WapiImageNTHeaders *nt_headers;
+       WapiImageResourceDirectory *resource_dir;
+       WapiImageResourceDirectoryEntry *resource_dir_entry;
+       guint32 resource_rva, entries, i;
+       gpointer ret = NULL;
+
+       dos_header = (WapiImageDosHeader *)file_map;
+       if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+#ifdef DEBUG
+               g_message ("%s: Bad dos signature 0x%x", __func__,
+                          dos_header->e_magic);
+#endif
+
+               SetLastError (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+       
+       if (map_size < sizeof(WapiImageNTHeaders) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+#ifdef DEBUG
+               g_message ("%s: File is too small: %d", __func__, map_size);
+#endif
+
+               SetLastError (ERROR_BAD_LENGTH);
+               return(NULL);
+       }
+       
+       nt_headers = (WapiImageNTHeaders *)GUINT_TO_POINTER ((GPOINTER_TO_UINT (file_map) + GUINT32_FROM_LE (dos_header->e_lfanew)));
+       if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+#ifdef DEBUG
+               g_message ("%s: Bad NT signature 0x%x", __func__,
+                          nt_headers->Signature);
+#endif
+
+               SetLastError (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+       
+       if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+               /* Do 64-bit stuff */
+               resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+       } else {
+               resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+       }
+
+       if (resource_rva == 0) {
+#ifdef DEBUG
+               g_message ("%s: No resources in file!", __func__);
+#endif
+               SetLastError (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+       
+       resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, nt_headers, file_map);
+       if (resource_dir == NULL) {
+#ifdef DEBUG
+               g_message ("%s: Can't find resource directory", __func__);
+#endif
+               SetLastError (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+       
+       entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
+       resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1);
+       
+       for (i = 0; i < entries; i++) {
+               WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
+               ret = scan_resource_dir (resource_dir, nt_headers, file_map,
+                                        direntry, 0, res_id, lang_id, size);
+               if (ret != NULL) {
+                       return(ret);
+               }
+       }
+       
+       return(NULL);
+}
+
+static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size)
+{
+       gchar *filename_ext;
+       int fd;
+       struct stat statbuf;
+       gpointer file_map;
+       
+       /* According to the MSDN docs, a search path is applied to
+        * filename.  FIXME: implement this, for now just pass it
+        * straight to fopen
+        */
+
+       filename_ext = mono_unicode_to_external (filename);
+       if (filename_ext == NULL) {
+#ifdef DEBUG
+               g_message ("%s: unicode conversion returned NULL", __func__);
+#endif
+
+               SetLastError (ERROR_INVALID_NAME);
+               return(NULL);
+       }
+       
+       fd = _wapi_open (filename_ext, O_RDONLY, 0);
+       if (fd == -1) {
+#ifdef DEBUG
+               g_message ("%s: Error opening file %s: %s", __func__,
+                          filename_ext, strerror (errno));
+#endif
+
+               SetLastError (_wapi_get_win32_file_error (errno));
+               g_free (filename_ext);
+               
+               return(NULL);
+       }
+
+       if (fstat (fd, &statbuf) == -1) {
+#ifdef DEBUG
+               g_message ("%s: Error stat()ing file %s: %s", __func__,
+                          filename_ext, strerror (errno));
+#endif
+
+               SetLastError (_wapi_get_win32_file_error (errno));
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+       *map_size = statbuf.st_size;
+
+       /* Check basic file size */
+       if (statbuf.st_size < sizeof(WapiImageDosHeader)) {
+#ifdef DEBUG
+               g_message ("%s: File %s is too small: %ld", __func__,
+                          filename_ext, statbuf.st_size);
+#endif
+
+               SetLastError (ERROR_BAD_LENGTH);
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+       
+       file_map = mmap (NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (file_map == MAP_FAILED) {
+#ifdef DEBUG
+               g_message ("%s: Error mmap()int file %s: %s", __func__,
+                          filename_ext, strerror (errno));
+#endif
+
+               SetLastError (_wapi_get_win32_file_error (errno));
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+
+       /* Don't need the fd any more */
+       close (fd);
+
+       return(file_map);
+}
+
+static void unmap_pe_file (gpointer file_map, guint32 map_size)
+{
+       munmap (file_map, map_size);
+}
+
+guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle)
+{
+       gpointer file_map;
+       gpointer versioninfo;
+       guint32 map_size;
+       guint32 size;
+       
+       /* This value is unused, but set to zero */
+       *handle = 0;
+       
+       file_map = map_pe_file (filename, &map_size);
+       if (file_map == NULL) {
+               return(0);
+       }
+       
+       versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+                                             0, &size);
+       if (versioninfo == NULL) {
+               /* Didn't find the resource, so set the return value
+                * to 0
+                */
+               size = 0;
+       }
+
+       unmap_pe_file (file_map, map_size);
+
+       return(size);
+}
+
+gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED,
+                            guint32 len, gpointer data)
+{
+       gpointer file_map;
+       gpointer versioninfo;
+       guint32 map_size;
+       guint32 size;
+       gboolean ret = FALSE;
+       
+       file_map = map_pe_file (filename, &map_size);
+       if (file_map == NULL) {
+               return(FALSE);
+       }
+       
+       versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+                                             0, &size);
+       if (versioninfo != NULL) {
+               /* This could probably process the data so that
+                * VerQueryValue() doesn't have to follow the data
+                * blocks every time.  But hey, these functions aren't
+                * likely to appear in many profiles.
+                */
+               memcpy (data, versioninfo, len < size?len:size);
+               ret = TRUE;
+       }
+
+       unmap_pe_file (file_map, map_size);
+       
+       return(ret);
+}
+
+static guint32 unicode_chars (const gunichar2 *str)
+{
+       guint32 len = 0;
+       
+       do {
+               if (str[len] == '\0') {
+                       return(len);
+               }
+               len++;
+       } while(1);
+}
+
+static gboolean unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
+{
+       while (*str1 && *str2) {
+               if (GUINT16_TO_LE (*str1) != GUINT16_TO_LE (*str2)) {
+                       return(FALSE);
+               }
+               ++str1;
+               ++str2;
+       }
+       
+       return(*str1 == *str2);
+}
+
+/* compare a little-endian null-terminated utf16 string and a normal string.
+ * Can be used only for ascii or latin1 chars.
+ */
+static gboolean unicode_string_equals (const gunichar2 *str1, const gchar *str2)
+{
+       while (*str1 && *str2) {
+               if (GUINT16_TO_LE (*str1) != *str2) {
+                       return(FALSE);
+               }
+               ++str1;
+               ++str2;
+       }
+       
+       return(*str1 == *str2);
+}
+
+typedef struct 
+{
+       guint16 data_len;
+       guint16 value_len;
+       guint16 type;
+       gunichar2 *key;
+} version_data;
+
+/* Returns a pointer to the value data, because there's no way to know
+ * how big that data is (value_len is set to zero for most blocks :-( )
+ */
+static gconstpointer get_versioninfo_block (gconstpointer data,
+                                           version_data *block)
+{
+       block->data_len = GUINT16_FROM_LE (*((guint16 *)data));
+       data = (char *)data + sizeof(guint16);
+       block->value_len = GUINT16_FROM_LE (*((guint16 *)data));
+       data = (char *)data + sizeof(guint16);
+       
+       /* No idea what the type is supposed to indicate */
+       block->type = GUINT16_FROM_LE (*((guint16 *)data));
+       data = (char *)data + sizeof(guint16);
+       block->key = ((gunichar2 *)data);
+       
+       /* Skip over the key (including the terminator) */
+       data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1);
+       
+       /* align on a 32-bit boundary */
+       data = (gpointer)((char *)data + 3);
+       data = (gpointer)((char *)data - (GPOINTER_TO_INT (data) & 3));
+       
+       return(data);
+}
+
+static gconstpointer get_fixedfileinfo_block (gconstpointer data,
+                                             version_data *block)
+{
+       gconstpointer data_ptr;
+       gint32 data_len; /* signed to guard against underflow */
+       WapiFixedFileInfo *ffi;
+
+       data_ptr = get_versioninfo_block (data, block);
+       data_len = block->data_len;
+               
+       if (block->value_len != sizeof(WapiFixedFileInfo)) {
+#ifdef DEBUG
+               g_message ("%s: FIXEDFILEINFO size mismatch", __func__);
+#endif
+               return(NULL);
+       }
+
+       if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) {
+#ifdef DEBUG
+               g_message ("%s: VS_VERSION_INFO mismatch", __func__);
+#endif
+               return(NULL);
+       }
+
+       ffi = ((WapiFixedFileInfo *)data_ptr);
+       if ((ffi->dwSignature != VS_FFI_SIGNATURE) ||
+           (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) {
+#ifdef DEBUG
+               g_message ("%s: FIXEDFILEINFO bad signature", __func__);
+#endif
+               return(NULL);
+       }
+
+       return(data_ptr);
+}
+
+static gconstpointer get_varfileinfo_block (gconstpointer data_ptr,
+                                           version_data *block)
+{
+       /* data is pointing at a Var block
+        */
+       data_ptr = get_versioninfo_block (data_ptr, block);
+
+       return(data_ptr);
+}
+
+static gconstpointer get_string_block (gconstpointer data_ptr,
+                                      const gunichar2 *string_key,
+                                      gpointer *string_value,
+                                      guint32 *string_value_len,
+                                      version_data *block)
+{
+       guint16 data_len = block->data_len;
+       guint16 string_len = 28; /* Length of the StringTable block */
+       
+       /* data_ptr is pointing at an array of one or more String blocks
+        * with total length (not including alignment padding) of
+        * data_len
+        */
+       while (string_len < data_len) {
+               gunichar2 *value;
+               
+               /* align on a 32-bit boundary */
+               data_ptr = (gpointer)((char *)data_ptr + 3);
+               data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+               
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+#ifdef DEBUG
+                       g_message ("%s: Hit 0-length block, giving up",
+                                  __func__);
+#endif
+                       return(NULL);
+               }
+               
+               string_len = string_len + block->data_len;
+               value = (gunichar2 *)data_ptr;
+               
+               if (string_key != NULL &&
+                   string_value != NULL &&
+                   string_value_len != NULL &&
+                   unicode_compare (string_key, block->key) == TRUE) {
+                       *string_value = (gpointer)data_ptr;
+                       *string_value_len = block->value_len;
+               }
+               
+               /* Skip over the value */
+               data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
+       }
+       
+       return(data_ptr);
+}
+
+/* Returns a pointer to the byte following the Stringtable block, or
+ * NULL if the data read hits padding.  We can't recover from this
+ * because the data length does not include padding bytes, so it's not
+ * possible to just return the start position + length
+ */
+static gconstpointer get_stringtable_block (gconstpointer data_ptr,
+                                           gunichar2 lang[8],
+                                           const gunichar2 *string_key,
+                                           gpointer *string_value,
+                                           guint32 *string_value_len,
+                                           version_data *block)
+{
+       guint16 data_len = block->data_len;
+       guint16 string_len = 36; /* length of the StringFileInfo block */
+       
+       /* data_ptr is pointing at an array of StringTable blocks,
+        * with total length (not including alignment padding) of
+        * data_len
+        */
+
+       while(string_len < data_len) {
+               /* align on a 32-bit boundary */
+               data_ptr = (gpointer)((char *)data_ptr + 3);
+               data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+               
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+#ifdef DEBUG
+                       g_message ("%s: Hit 0-length block, giving up",
+                                  __func__);
+#endif
+                       return(NULL);
+               }
+               
+               string_len = string_len + block->data_len;
+               
+               if (!memcmp (block->key, lang, 8 * sizeof(gunichar2))) {
+                       /* Got the one we're interested in */
+                       data_ptr = get_string_block (data_ptr, string_key,
+                                                    string_value,
+                                                    string_value_len, block);
+               } else {
+                       data_ptr = get_string_block (data_ptr, NULL, NULL,
+                                                    NULL, block);
+               }
+               
+               if (data_ptr == NULL) {
+                       /* Child block hit padding */
+#ifdef DEBUG
+                       g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+                       return(NULL);
+               }
+       }
+       
+       return(data_ptr);
+}
+
+gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock,
+                       gpointer *buffer, guint32 *len)
+{
+       gchar *subblock_utf8;
+       gboolean ret = FALSE;
+       version_data block;
+       gconstpointer data_ptr;
+       gint32 data_len; /* signed to guard against underflow */
+       gboolean want_var = FALSE;
+       gboolean want_string = FALSE;
+       gunichar2 lang[8];
+       const gunichar2 *string_key = NULL;
+       gpointer string_value = NULL;
+       guint32 string_value_len = 0;
+       
+       subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL);
+       if (subblock_utf8 == NULL) {
+               return(FALSE);
+       }
+
+       if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) {
+               want_var = TRUE;
+       } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) {
+               want_string = TRUE;
+               memcpy (lang, subblock + 16, 8 * sizeof(gunichar2));
+               string_key = subblock + 25;
+       }
+       
+       if (!strcmp (subblock_utf8, "\\")) {
+               data_ptr = get_fixedfileinfo_block (datablock, &block);
+               if (data_ptr != NULL) {
+                       *buffer = (gpointer)data_ptr;
+                       *len = block.value_len;
+               
+                       ret = TRUE;
+               }
+       } else if (want_var || want_string) {
+               data_ptr = get_fixedfileinfo_block (datablock, &block);
+               if (data_ptr != NULL) {
+                       /* The FFI and header occupies the first 92
+                        * bytes
+                        */
+                       data_ptr = (char *)data_ptr + sizeof(WapiFixedFileInfo);
+                       data_len = block.data_len - 92;
+                       
+                       /* There now follow zero or one StringFileInfo
+                        * blocks and zero or one VarFileInfo blocks
+                        */
+                       while (data_len > 0) {
+                               /* align on a 32-bit boundary */
+                               data_ptr = (gpointer)((char *)data_ptr + 3);
+                               data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+                               
+                               data_ptr = get_versioninfo_block (data_ptr,
+                                                                 &block);
+                               if (block.data_len == 0) {
+                                       /* We must have hit padding,
+                                        * so give up processing now
+                                        */
+#ifdef DEBUG
+                                       g_message ("%s: Hit 0-length block, giving up", __func__);
+#endif
+                                       goto done;
+                               }
+                               
+                               data_len = data_len - block.data_len;
+                               
+                               if (unicode_string_equals (block.key, "VarFileInfo")) {
+                                       data_ptr = get_varfileinfo_block (data_ptr, &block);
+                                       if (want_var) {
+                                               *buffer = (gpointer)data_ptr;
+                                               *len = block.value_len;
+                                               ret = TRUE;
+                                               goto done;
+                                       } else {
+                                               /* Skip over the Var block */
+                                               data_ptr = ((guchar *)data_ptr) + block.value_len;
+                                       }
+                               } else if (unicode_string_equals (block.key, "StringFileInfo")) {
+                                       data_ptr = get_stringtable_block (data_ptr, lang, string_key, &string_value, &string_value_len, &block);
+                                       if (want_string &&
+                                           string_value != NULL &&
+                                           string_value_len != 0) {
+                                               *buffer = string_value;
+                                               *len = string_value_len;
+                                               ret = TRUE;
+                                               goto done;
+                                       }
+                               } else {
+                                       /* Bogus data */
+#ifdef DEBUG
+                                       g_message ("%s: Not a valid VERSIONINFO child block", __func__);
+#endif
+                                       goto done;
+                               }
+                               
+                               if (data_ptr == NULL) {
+                                       /* Child block hit padding */
+#ifdef DEBUG
+                                       g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+                                       goto done;
+                               }
+                       }
+               }
+       }
+
+  done:
+       g_free (subblock_utf8);
+       return(ret);
+}
+
+guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
+{
+       return(0);
+}
index 823ababc74a0d0737127c1e0e672a5d12cc9362b..1cd814cb465b0b349225f0b7ef9bed16daa1e906 100644 (file)
@@ -98,6 +98,287 @@ typedef struct
        guint32 dwFileDateLS;
 } WapiFixedFileInfo;
 
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+#define VS_FFI_SIGNATURE       0xbd04effe
+#define VS_FFI_STRUCVERSION    0x00000100
+#else
+#define VS_FFI_SIGNATURE       0xfeef04bd
+#define VS_FFI_STRUCVERSION    0x00010000
+#endif
+
+#define VS_FFI_FILEFLAGSMASK   0x3f
+
+typedef struct
+{
+       gpointer lpBaseOfDll;
+       guint32 SizeOfImage;
+       gpointer EntryPoint;
+} WapiModuleInfo;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT   0
+#define IMAGE_DIRECTORY_ENTRY_IMPORT   1
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION        3
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC        5
+#define IMAGE_DIRECTORY_ENTRY_DEBUG    6
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT        7
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE     7
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR        8
+#define IMAGE_DIRECTORY_ENTRY_TLS      9
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG      10
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT     11
+#define IMAGE_DIRECTORY_ENTRY_IAT      12
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT     13
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR   14
+
+#define IMAGE_SIZEOF_SHORT_NAME        8
+
+#define IMAGE_RESOURCE_NAME_IS_STRING          0x80000000
+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY       0x80000000
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+#define IMAGE_DOS_SIGNATURE    0x4d5a
+#define IMAGE_NT_SIGNATURE     0x50450000
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC  0xb10
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC  0xb20
+#else
+#define IMAGE_DOS_SIGNATURE    0x5a4d
+#define IMAGE_NT_SIGNATURE     0x00004550
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC  0x10b
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC  0x20b
+#endif
+
+typedef struct
+{
+       guint16 e_magic;
+       guint16 e_cblp;
+       guint16 e_cp;
+       guint16 e_crlc;
+       guint16 e_cparhdr;
+       guint16 e_minalloc;
+       guint16 e_maxalloc;
+       guint16 e_ss;
+       guint16 e_sp;
+       guint16 e_csum;
+       guint16 e_ip;
+       guint16 e_cs;
+       guint16 e_lfarlc;
+       guint16 e_ovno;
+       guint16 e_res[4];
+       guint16 e_oemid;
+       guint16 e_oeminfo;
+       guint16 e_res2[10];
+       guint32 e_lfanew;
+} WapiImageDosHeader;
+
+typedef struct
+{
+       guint16 Machine;
+       guint16 NumberOfSections;
+       guint32 TimeDateStamp;
+       guint32 PointerToSymbolTable;
+       guint32 NumberOfSymbols;
+       guint16 SizeOfOptionalHeader;
+       guint16 Characteristics;
+} WapiImageFileHeader;
+
+typedef struct
+{
+       guint32 VirtualAddress;
+       guint32 Size;
+} WapiImageDataDirectory;
+
+typedef struct
+{
+       guint16 Magic;
+       guint8 MajorLinkerVersion;
+       guint8 MinorLinkerVersion;
+       guint32 SizeOfCode;
+       guint32 SizeOfInitializedData;
+       guint32 SizeOfUninitializedData;
+       guint32 AddressOfEntryPoint;
+       guint32 BaseOfCode;
+       guint32 BaseOfData;
+       guint32 ImageBase;
+       guint32 SectionAlignment;
+       guint32 FileAlignment;
+       guint16 MajorOperatingSystemVersion;
+       guint16 MinorOperatingSystemVersion;
+       guint16 MajorImageVersion;
+       guint16 MinorImageVersion;
+       guint16 MajorSubsystemVersion;
+       guint16 MinorSubsystemVersion;
+       guint32 Win32VersionValue;
+       guint32 SizeOfImage;
+       guint32 SizeOfHeaders;
+       guint32 CheckSum;
+       guint16 Subsystem;
+       guint16 DllCharacteristics;
+       guint32 SizeOfStackReserve;
+       guint32 SizeOfStackCommit;
+       guint32 SizeOfHeapReserve;
+       guint32 SizeOfHeapCommit;
+       guint32 LoaderFlags;
+       guint32 NumberOfRvaAndSizes;
+       WapiImageDataDirectory DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} WapiImageOptionalHeader32;
+
+typedef struct
+{
+       guint16 Magic;
+       guint8 MajorLinkerVersion;
+       guint8 MinorLinkerVersion;
+       guint32 SizeOfCode;
+       guint32 SizeOfInitializedData;
+       guint32 SizeOfUninitializedData;
+       guint32 AddressOfEntryPoint;
+       guint32 BaseOfCode;
+       guint64 ImageBase;
+       guint32 SectionAlignment;
+       guint32 FileAlignment;
+       guint16 MajorOperatingSystemVersion;
+       guint16 MinorOperatingSystemVersion;
+       guint16 MajorImageVersion;
+       guint16 MinorImageVersion;
+       guint16 MajorSubsystemVersion;
+       guint16 MinorSubsystemVersion;
+       guint32 Win32VersionValue;
+       guint32 SizeOfImage;
+       guint32 SizeOfHeaders;
+       guint32 CheckSum;
+       guint16 Subsystem;
+       guint16 DllCharacteristics;
+       guint64 SizeOfStackReserve;
+       guint64 SizeOfStackCommit;
+       guint64 SizeOfHeapReserve;
+       guint64 SizeOfHeapCommit;
+       guint32 LoaderFlags;
+       guint32 NumberOfRvaAndSizes;
+       WapiImageDataDirectory DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} WapiImageOptionalHeader64;
+
+#if SIZEOF_VOID_P == 8
+typedef WapiImageOptionalHeader64      WapiImageOptionalHeader;
+#else
+typedef WapiImageOptionalHeader32      WapiImageOptionalHeader;
+#endif
+
+typedef struct
+{
+       guint32 Signature;
+       WapiImageFileHeader FileHeader;
+       WapiImageOptionalHeader OptionalHeader;
+} WapiImageNTHeaders32;
+
+typedef struct
+{
+       guint32 Signature;
+       WapiImageFileHeader FileHeader;
+       WapiImageOptionalHeader64 OptionalHeader;
+} WapiImageNTHeaders64;
+
+#if SIZEOF_VOID_P == 8
+typedef WapiImageNTHeaders64   WapiImageNTHeaders;
+#else
+typedef WapiImageNTHeaders32   WapiImageNTHeaders;
+#endif
+
+typedef struct
+{
+       guint8 Name[IMAGE_SIZEOF_SHORT_NAME];
+       union
+       {
+               guint32 PhysicalAddress;
+               guint32 VirtualSize;
+       } Misc;
+       guint32 VirtualAddress;
+       guint32 SizeOfRawData;
+       guint32 PointerToRawData;
+       guint32 PointerToRelocations;
+       guint32 PointerToLinenumbers;
+       guint16 NumberOfRelocations;
+       guint16 NumberOfLinenumbers;
+       guint32 Characteristics;
+} WapiImageSectionHeader;
+
+#define IMAGE_FIRST_SECTION(header) ((WapiImageSectionHeader *)(GPOINTER_TO_UINT (header) + G_STRUCT_OFFSET (WapiImageNTHeaders, OptionalHeader) + ((WapiImageNTHeaders *)(header))->FileHeader.SizeOfOptionalHeader))
+
+#define RT_CURSOR      0x01
+#define RT_BITMAP      0x02
+#define RT_ICON                0x03
+#define RT_MENU                0x04
+#define RT_DIALOG      0x05
+#define RT_STRING      0x06
+#define RT_FONTDIR     0x07
+#define RT_FONT                0x08
+#define RT_ACCELERATOR 0x09
+#define RT_RCDATA      0x0a
+#define RT_MESSAGETABLE        0x0b
+#define RT_GROUP_CURSOR        0x0c
+#define RT_GROUP_ICON  0x0e
+#define RT_VERSION     0x10
+#define RT_DLGINCLUDE  0x11
+#define RT_PLUGPLAY    0x13
+#define RT_VXD         0x14
+#define RT_ANICURSOR   0x15
+#define RT_ANIICON     0x16
+#define RT_HTML                0x17
+#define RT_MANIFEST    0x18
+
+typedef struct
+{
+       guint32 Characteristics;
+       guint32 TimeDateStamp;
+       guint16 MajorVersion;
+       guint16 MinorVersion;
+       guint16 NumberOfNamedEntries;
+       guint16 NumberOfIdEntries;
+} WapiImageResourceDirectory;
+
+typedef struct
+{
+       union 
+       {
+               struct 
+               {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+                       guint32 NameIsString:1;
+                       guint32 NameOffset:31;
+#else
+                       guint32 NameOffset:31;
+                       guint32 NameIsString:1;
+#endif
+               };
+               guint32 Name;
+               guint16 Id;
+       };
+       union
+       {
+               guint32 OffsetToData;
+               struct 
+               {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+                       guint32 DataIsDirectory:1;
+                       guint32 OffsetToDirectory:31;
+#else
+                       guint32 OffsetToDirectory:31;
+                       guint32 DataIsDirectory:1;
+#endif
+               };
+       };
+} WapiImageResourceDirectoryEntry;
+
+typedef struct 
+{
+       guint32 OffsetToData;
+       guint32 Size;
+       guint32 CodePage;
+       guint32 Reserved;
+} WapiImageResourceDataEntry;
+
 #define VS_FF_DEBUG            0x0001
 #define VS_FF_PRERELEASE       0x0002
 #define VS_FF_PATCHED          0x0004
@@ -147,14 +428,13 @@ typedef struct
 #define VFT2_FONT_VECTOR       0x0002
 #define VFT2_FONT_TRUETYPE     0x0003
 
-#if G_BYTE_ORDER != G_LITTLE_ENDIAN
-#define VS_FFI_SIGNATURE       0xbd04effe
-#define VS_FFI_STRUCVERSION    0x00000100
-#else
-#define VS_FFI_SIGNATURE       0xfeef04bd
-#define VS_FFI_STRUCVERSION    0x00010000
-#endif
-
-#define VS_FFI_FILEFLAGSMASK   0x3f
+extern guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle);
+extern gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle,
+                                   guint32 len, gpointer data);
+extern gboolean VerQueryValue (gconstpointer datablock,
+                              const gunichar2 *subblock, gpointer *buffer,
+                              guint32 *len);
+extern guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out,
+                               guint32 lang_len);
 
 #endif /* _WAPI_VERSIONINFO_H_ */
index 38ebf3f0ce89dd1c4895c75e74a72bdc775413f0..e19399b9f6eb8ec76fc6e458458f5f2ca26ca4bd 100644 (file)
@@ -1,3 +1,11 @@
+2007-11-15  Dick Porter  <dick@ximian.com>
+
+       * process.c: Read file version info from the files pointed at by
+       process modules, not the current process.  Fixes bug 315969.
+
+       Use windows typedef names in some places to fix warnings on the
+       windows build.
+
 2007-11-15  Mark Probst  <mark.probst@gmail.com>
 
        * image.c, metadata-internals.h: Added a generic_class_cache hash
index 4198a1dc621b543b13609c0bb50a01d255465406..faa88112c2fb93cd7ee80a2eb575e73f8ff78971 100644 (file)
@@ -168,7 +168,7 @@ ICALL_TYPE(PROCESS, "System.Diagnostics.Process", PROCESS_1)
 ICALL(PROCESS_1, "CreateProcess_internal(System.Diagnostics.ProcessStartInfo,intptr,intptr,intptr,System.Diagnostics.Process/ProcInfo&)", ves_icall_System_Diagnostics_Process_CreateProcess_internal)
 ICALL(PROCESS_2, "ExitCode_internal(intptr)", ves_icall_System_Diagnostics_Process_ExitCode_internal)
 ICALL(PROCESS_3, "ExitTime_internal(intptr)", ves_icall_System_Diagnostics_Process_ExitTime_internal)
-ICALL(PROCESS_4, "GetModules_internal()", ves_icall_System_Diagnostics_Process_GetModules_internal)
+ICALL(PROCESS_4, "GetModules_internal(intptr)", ves_icall_System_Diagnostics_Process_GetModules_internal)
 ICALL(PROCESS_5, "GetPid_internal()", ves_icall_System_Diagnostics_Process_GetPid_internal)
 ICALL(PROCESS_5B, "GetPriorityClass(intptr,int&)", ves_icall_System_Diagnostics_Process_GetPriorityClass)
 ICALL(PROCESS_6, "GetProcess_internal(int)", ves_icall_System_Diagnostics_Process_GetProcess_internal)
index 118fe66834d27d6083de946d4fbe447c224e4bb3..89bbbf63ab6dd2b2c6a7f2d66ce72329256a87d0 100644 (file)
@@ -85,52 +85,6 @@ static guint32 unicode_chars (const gunichar2 *str)
        } while(1);
 }
 
-static guint32 unicode_bytes (const gunichar2 *str)
-{
-       guint32 len=0;
-       
-       do {
-               if(str[len]=='\0') {
-                       /* Include the terminators */
-                       return((len*2)+2);
-               }
-               len++;
-       } while(1);
-}
-
-static gunichar2*
-unicode_get (const gunichar2 *str)
-{
-       gunichar2 *swapped;
-       int i, len;
-
-       len = unicode_bytes (str);
-       swapped = g_malloc0 (len);
-       i = 0;
-       while (str [i]) {
-               swapped [i] = GUINT16_FROM_LE (str [i]);
-               i ++;
-       }
-
-       return swapped;
-}
-
-/* 
- * compare a little-endian null-terminated utf16 string and a normal string.
- * Can be used only for ascii or latin1 chars.
- */
-static gboolean
-unicode_string_equals (const gunichar2 *str1, const gchar *str2)
-{
-       while (*str1 && *str2) {
-               if (GUINT16_TO_LE (*str1) != *str2)
-                       return FALSE;
-               ++str1;
-               ++str2;
-       }
-       return *str1 == *str2;
-}
-
 static void process_set_field_object (MonoObject *obj, const gchar *fieldname,
                                      MonoObject *data)
 {
@@ -166,39 +120,33 @@ static void process_set_field_string (MonoObject *obj, const gchar *fieldname,
        *(MonoString **)(((char *)obj) + field->offset)=string;
 }
 
-static void process_set_field_string_utf8 (MonoObject *obj,
-                                          const gchar *fieldname,
-                                          const gchar *val)
+static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
+                                  guint32 val)
 {
        MonoClassField *field;
-       MonoString *string;
 
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
+       g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
                   fieldname, val);
 #endif
-
-       string=mono_string_new (mono_object_domain (obj), val);
        
        field=mono_class_get_field_from_name (mono_object_class (obj),
                                              fieldname);
-       /* FIXME: moving GC */
-       *(MonoString **)(((char *)obj) + field->offset)=string;
+       *(guint32 *)(((char *)obj) + field->offset)=val;
 }
 
-static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
-                                  guint32 val)
+static void process_set_field_intptr (MonoObject *obj, const gchar *fieldname,
+                                     gpointer val)
 {
        MonoClassField *field;
 
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
-                  fieldname, val);
+       g_message ("%s: Setting field %s to %p", __func__, fieldname, val);
 #endif
        
        field=mono_class_get_field_from_name (mono_object_class (obj),
                                              fieldname);
-       *(guint32 *)(((char *)obj) + field->offset)=val;
+       *(gpointer *)(((char *)obj) + field->offset)=val;
 }
 
 static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
@@ -216,378 +164,186 @@ static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
        *(guint8 *)(((char *)obj) + field->offset)=val;
 }
 
-typedef struct {
-       guint16 data_len;
-       guint16 value_len;
-       guint16 type;
-       gunichar2 *key;
-} version_data;
-
-/* Returns a pointer to the value data, because theres no way to know
- * how big that data is (value_len is set to zero for most blocks :-()
- */
-static gpointer process_get_versioninfo_block (gpointer data,
-                                              version_data *block)
-{
-       block->data_len=GUINT16_TO_LE (*((guint16 *)data));
-       data = (char *)data + sizeof(guint16);
-       block->value_len=GUINT16_TO_LE (*((guint16 *)data));
-       data = (char *)data + sizeof(guint16);
-
-       /* No idea what the type is supposed to indicate */
-       block->type=GUINT16_TO_LE (*((guint16 *)data));
-       data = (char *)data + sizeof(guint16);
-       block->key=((gunichar2 *)data);
-
-       /* skip over the key (including the terminator) */
-       data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
-
-       /* align on a 32-bit boundary */
-       data=(gpointer)((char *)data + 3);
-       data=(gpointer)((char *)data - (GPOINTER_TO_INT(data) & 3));
-       
-       return(data);
-}
+#define SFI_COMMENTS           "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
+#define SFI_COMPANYNAME                "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
+#define SFI_FILEDESCRIPTION    "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
+#define SFI_FILEVERSION                "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
+#define SFI_INTERNALNAME       "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
+#define SFI_LEGALCOPYRIGHT     "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
+#define SFI_LEGALTRADEMARKS    "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
+#define SFI_ORIGINALFILENAME   "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
+#define SFI_PRIVATEBUILD       "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
+#define SFI_PRODUCTNAME                "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
+#define SFI_PRODUCTVERSION     "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
+#define SFI_SPECIALBUILD       "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
 
-/* Returns a pointer to the byte following the Var block */
-static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
-                                       guint16 data_len)
+static void process_module_string_read (MonoObject *filever, gpointer data,
+                                       const gchar *fieldname,
+                                       guchar lang_hi, guchar lang_lo,
+                                       const gchar *key)
 {
-       /* Not currently interested in the VarFileInfo block.  This
-        * might change if language support is needed for file version
-        * strings (VarFileInfo contains lists of supported
-        * languages.)
-        */
-       version_data block;
+       gchar *lang_key_utf8;
+       gunichar2 *lang_key, *buffer;
+       UINT bytes;
 
-       /* data_ptr is pointing at a Var block of length data_len */
-       data_ptr=process_get_versioninfo_block (data_ptr, &block);
-       data_ptr=((guchar *)data_ptr)+block.value_len;
-
-       return(data_ptr);
-}
-
-/* Returns a pointer to the byte following the String block, or NULL
- * if the data read hits padding.  We can't recover from this because
- * the data length does not include padding bytes, so it's not
- * possible to just return the start position + length.
- */
-static gpointer process_read_string_block (MonoObject *filever,
-                                          gpointer data_ptr,
-                                          guint16 data_len,
-                                          gboolean store)
-{
-       version_data block;
-       guint16 string_len=28; /* Length of the StringTable block */
+       lang_key_utf8 = g_strdup_printf (key, lang_lo, lang_hi, 0x04, 0xb0);
 
-       /* data_ptr is pointing at an array of one or more String
-        * blocks with total length (not including alignment padding)
-        * of data_len.
-        */
-       while(string_len<data_len) {
-               gunichar2 *value;
-               
-               /* align on a 32-bit boundary */
-               data_ptr=(gpointer)((char *)data_ptr + 3);
-               data_ptr=(gpointer)((char *)data_ptr -
-                   (GPOINTER_TO_INT(data_ptr) & 3));
-
-               data_ptr=process_get_versioninfo_block (data_ptr, &block);
-               if(block.data_len==0) {
-                       /* We must have hit padding, so give up
-                        * processing now
-                        */
 #ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION
-                                  ": Hit 0-length block, giving up");
+       g_message ("%s: asking for [%s]", __func__, lang_key_utf8);
 #endif
-                       return(NULL);
-               }
-               
-               string_len=string_len+block.data_len;
-               value=unicode_get ((gunichar2 *)data_ptr);
-               /* Skip over the value */
-               data_ptr=((gunichar2 *)data_ptr)+block.value_len;
-
-               if(store==TRUE) {
-                       if (unicode_string_equals (block.key, "Comments")) {
-                               process_set_field_string (filever, "comments", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "CompanyName")) {
-                               process_set_field_string (filever, "companyname", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "FileDescription")) {
-                               process_set_field_string (filever, "filedescription", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "FileVersion")) {
-                               process_set_field_string (filever, "fileversion", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "InternalName")) {
-                               process_set_field_string (filever, "internalname", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "LegalCopyright")) {
-                               process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "LegalTrademarks")) {
-                               process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "OriginalFilename")) {
-                               process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "PrivateBuild")) {
-                               process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "ProductName")) {
-                               process_set_field_string (filever, "productname", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "ProductVersion")) {
-                               process_set_field_string (filever, "productversion", value, unicode_chars (value));
-                       } else if (unicode_string_equals (block.key, "SpecialBuild")) {
-                               process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
-                       } else {
-                               /* Not an error, just not interesting
-                                * in this case
-                                */
-                       }
-               }
-               g_free (value);
-       }
-       
-       return(data_ptr);
-}
 
-/* returns a pointer to the byte following the Stringtable block, or
- * NULL if the data read hits padding.  We can't recover from this
- * because the data length does not include padding bytes, so it's not
- * possible to just return the start position + length
- */
-static gpointer process_read_stringtable_block (MonoObject *filever,
-                                               gpointer data_ptr,
-                                               guint16 data_len)
-{
-       version_data block;
-       gunichar2 *value;
-       gchar *language;
-       guint16 string_len=36;  /* length of the StringFileInfo block */
-
-       /* data_ptr is pointing at an array of StringTable blocks,
-        * with total length (not including alignment padding) of
-        * data_len.
-        */
-
-       while(string_len<data_len) {
-               /* align on a 32-bit boundary */
-               data_ptr=(gpointer)((char *)data_ptr + 3);
-               data_ptr=(gpointer)((char *)data_ptr -
-                   (GPOINTER_TO_INT(data_ptr) & 3));
-
-               data_ptr=process_get_versioninfo_block (data_ptr, &block);
-               if(block.data_len==0) {
-                       /* We must have hit padding, so give up
-                        * processing now
-                        */
-#ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION
-                                  ": Hit 0-length block, giving up");
-#endif
-                       return(NULL);
-               }
-               string_len=string_len+block.data_len;
-
-               value = unicode_get (block.key);
-               language = g_utf16_to_utf8 (value, unicode_bytes (block.key), NULL, NULL, NULL);
-               g_strdown (language);
-
-               /* Kludge: treat en_US as neutral too */
-               if (!strcmp (language, "007f04b0") ||
-                   !strcmp (language, "000004b0") ||
-                   !strcmp (language, "040904b0")) {
-                       /* Got the one we're interested in */
-                       process_set_field_string_utf8 (filever, "language",
-                                                      "Language Neutral");
-                       
-                       data_ptr=process_read_string_block (filever, data_ptr,
-                                                           block.data_len,
-                                                           TRUE);
-               } else {
-                       /* Some other language.  We might want to do
-                        * something with this in the future.
-                        */
-                       data_ptr=process_read_string_block (filever, data_ptr,
-                                                           block.data_len,
-                                                           FALSE);
-               }
-               g_free (language);
-               g_free (value);
-               
-               if(data_ptr==NULL) {
-                       /* Child block hit padding */
-#ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
-#endif
-                       return(NULL);
-               }
-       }
-               
-       return(data_ptr);
-}
+       lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL);
 
-static void process_read_fixedfileinfo_block (MonoObject *filever,
-                                             VS_FIXEDFILEINFO *ffi)
-{
+       if (VerQueryValue (data, lang_key, (gpointer *)&buffer, &bytes)) {
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": ffi: sig 0x%x, strucver 0x%x, fileverm 0x%x, fileverl 0x%x, prodverm 0x%x, prodverl 0x%x, ffmask 0x%x, ff 0x%x, os 0x%x, type 0x%x, subtype 0x%x, datem 0x%x, datel 0x%x", ffi->dwSignature, ffi->dwStrucVersion, ffi->dwFileVersionMS, ffi->dwFileVersionLS, ffi->dwProductVersionMS, ffi->dwProductVersionLS, ffi->dwFileFlagsMask, ffi->dwFileFlags, ffi->dwFileOS, ffi->dwFileType, ffi->dwFileSubtype, ffi->dwFileDateMS, ffi->dwFileDateLS);
+               g_message ("%s: found %d bytes of [%s]", __func__, bytes,
+                          g_utf16_to_utf8 (buffer, bytes, NULL, NULL, NULL));
 #endif
-               
-       process_set_field_int (filever, "filemajorpart",
-                              HIWORD (ffi->dwFileVersionMS));
-       process_set_field_int (filever, "fileminorpart",
-                              LOWORD (ffi->dwFileVersionMS));
-       process_set_field_int (filever, "filebuildpart",
-                              HIWORD (ffi->dwFileVersionLS));
-       process_set_field_int (filever, "fileprivatepart",
-                              LOWORD (ffi->dwFileVersionLS));
-               
-       process_set_field_int (filever, "productmajorpart",
-                              HIWORD (ffi->dwProductVersionMS));
-       process_set_field_int (filever, "productminorpart",
-                              LOWORD (ffi->dwProductVersionMS));
-       process_set_field_int (filever, "productbuildpart",
-                              HIWORD (ffi->dwProductVersionLS));
-       process_set_field_int (filever, "productprivatepart",
-                              LOWORD (ffi->dwProductVersionLS));
-       
-       process_set_field_bool (filever, "isdebug",
-                               ffi->dwFileFlags&VS_FF_DEBUG);
-       process_set_field_bool (filever, "isprerelease",
-                               ffi->dwFileFlags&VS_FF_PRERELEASE);
-       process_set_field_bool (filever, "ispatched",
-                               ffi->dwFileFlags&VS_FF_PATCHED);
-       process_set_field_bool (filever, "isprivatebuild",
-                               ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
-       process_set_field_bool (filever, "isspecialbuild",
-                               ffi->dwFileFlags&VS_FF_SPECIALBUILD);
-}
-
-static void process_get_fileversion (MonoObject *filever, MonoImage *image)
-{
-       MonoPEResourceDataEntry *version_info;
-       gpointer data;
+               process_set_field_string (filever, fieldname, buffer, bytes);
+       }
+
+       g_free (lang_key);
+       g_free (lang_key_utf8);
+}
+
+static void process_module_stringtable (MonoObject *filever, gpointer data,
+                                       guchar lang_hi, guchar lang_lo)
+{
+       process_module_string_read (filever, data, "comments", lang_hi, lang_lo,
+                                   SFI_COMMENTS);
+       process_module_string_read (filever, data, "companyname", lang_hi,
+                                   lang_lo, SFI_COMPANYNAME);
+       process_module_string_read (filever, data, "filedescription", lang_hi,
+                                   lang_lo, SFI_FILEDESCRIPTION);
+       process_module_string_read (filever, data, "fileversion", lang_hi,
+                                   lang_lo, SFI_FILEVERSION);
+       process_module_string_read (filever, data, "internalname", lang_hi,
+                                   lang_lo, SFI_INTERNALNAME);
+       process_module_string_read (filever, data, "legalcopyright", lang_hi,
+                                   lang_lo, SFI_LEGALCOPYRIGHT);
+       process_module_string_read (filever, data, "legaltrademarks", lang_hi,
+                                   lang_lo, SFI_LEGALTRADEMARKS);
+       process_module_string_read (filever, data, "originalfilename", lang_hi,
+                                   lang_lo, SFI_ORIGINALFILENAME);
+       process_module_string_read (filever, data, "privatebuild", lang_hi,
+                                   lang_lo, SFI_PRIVATEBUILD);
+       process_module_string_read (filever, data, "productname", lang_hi,
+                                   lang_lo, SFI_PRODUCTNAME);
+       process_module_string_read (filever, data, "productversion", lang_hi,
+                                   lang_lo, SFI_PRODUCTVERSION);
+       process_module_string_read (filever, data, "specialbuild", lang_hi,
+                                   lang_lo, SFI_SPECIALBUILD);
+}
+
+static void process_get_fileversion (MonoObject *filever, gunichar2 *filename)
+{
+       DWORD verinfohandle;
        VS_FIXEDFILEINFO *ffi;
-       gpointer data_ptr;
-       version_data block;
-       gint32 data_len; /* signed to guard against underflow */
-
-       version_info=mono_image_lookup_resource (image,
-                                                MONO_PE_RESOURCE_ID_VERSION,
-                                                0, NULL);
+       gpointer data;
+       DWORD datalen;
+       guchar *trans_data;
+       gunichar2 *query;
+       UINT ffi_size, trans_size;
+       BOOL ok;
+       int i;
+
+       datalen = GetFileVersionInfoSize (filename, &verinfohandle);
+       if (datalen) {
+               data = g_malloc0 (datalen);
+               ok = GetFileVersionInfo (filename, verinfohandle, datalen,
+                                        data);
+               if (ok) {
+                       query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL);
+                       if (query == NULL) {
+                               g_free (data);
+                               return;
+                       }
+                       
+                       if (VerQueryValue (data, query, (gpointer *)&ffi,
+                           &ffi_size)) {
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
-                  version_info);
+                               g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), HIWORD (ffi->dwFileVersionMS), LOWORD (ffi->dwFileVersionMS), HIWORD (ffi->dwFileVersionLS), LOWORD (ffi->dwFileVersionLS));
 #endif
-
-       if(version_info==NULL) {
-               return;
-       }
        
-       data=mono_image_rva_map (image,
-                              version_info->rde_data_offset);
-       g_free (version_info);
-       if(data==NULL) {
-               return;
-       }
-
-       /* See io-layer/versioninfo.h for the gory details on how this
-        * data is laid out. (data should be pointing to
-        * VS_VERSIONINFO data).
-        */
-
-       data_ptr=process_get_versioninfo_block (data, &block);
-               
-       data_len=block.data_len;
-               
-       if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
-#ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          ": FIXEDFILEINFO size mismatch");
-#endif
-               return;
-       }
-
-       if (!unicode_string_equals (block.key, "VS_VERSION_INFO")) {
-#ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          ": VS_VERSION_INFO mismatch");
-#endif
-               return;
-       }
+                               process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS));
+                               process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS));
+                               process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS));
+                               process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS));
+
+                               process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS));
+                               process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS));
+                               process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS));
+                               process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS));
+
+                               process_set_field_bool (filever, "isdebug", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG);
+                               process_set_field_bool (filever, "isprerelease", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE);
+                               process_set_field_bool (filever, "ispatched", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED);
+                               process_set_field_bool (filever, "isprivatebuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD);
+                               process_set_field_bool (filever, "isspecialbuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD);
+                       }
+                       g_free (query);
 
-       ffi=((VS_FIXEDFILEINFO *)data_ptr);
-       data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
-       if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
-          (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
-#ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          ": FIXEDFILEINFO bad signature");
-#endif
-               return;
-       }
-       process_read_fixedfileinfo_block (filever, ffi);
-       
-       /* Subtract the 92 bytes we've already seen */
-       data_len -= 92;
-       
-       /* There now follow zero or one StringFileInfo blocks and zero
-        * or one VarFileInfo blocks
-        */
-       while(data_len > 0) {
-               /* align on a 32-bit boundary */
-               data_ptr=(gpointer)((char *)data_ptr + 3);
-               data_ptr=(gpointer)((char *)data_ptr -
-                   (GPOINTER_TO_INT(data_ptr) & 3));
-
-               data_ptr=process_get_versioninfo_block (data_ptr, &block);
-               if(block.data_len==0) {
-                       /* We must have hit padding, so give up
-                        * processing now
-                        */
-#ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION
-                                  ": Hit 0-length block, giving up");
-#endif
-                       return;
-               }
-               
-               data_len=data_len-block.data_len;
-
-               if (unicode_string_equals (block.key, "VarFileInfo")) {
-                       data_ptr=process_read_var_block (filever, data_ptr,
-                                                        block.data_len);
-               } else if (unicode_string_equals (block.key, "StringFileInfo")) {
-                       data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
-               } else {
-                       /* Bogus data */
+                       query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL);
+                       if (query == NULL) {
+                               g_free (data);
+                               return;
+                       }
+                       
+                       if (VerQueryValue (data, query,
+                                          (gpointer *)&trans_data,
+                                          &trans_size)) {
+                               /* Look for neutral or en_US language data
+                                * (or should we use the first language ID we
+                                * see?)
+                                */
+                               for (i = 0; i < trans_size; i += 4) {
 #ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION
-                                  ": Not a valid VERSIONINFO child block");
-                       return;
+                                       g_message("%s: %s has 0x%0x 0x%0x 0x%0x 0x%0x", __func__, g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), trans_data[i], trans_data[i+1], trans_data[i+2], trans_data[i+3]);
 #endif
-               }
 
-               if(data_ptr==NULL) {
-                       /* Child block hit padding */
-#ifdef DEBUG
-                       g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
-#endif
-                       return;
+                                       if ((trans_data[i] == 0x09 &&
+                                            trans_data[i+1] == 0x04 &&
+                                            trans_data[i+2] == 0xb0 &&
+                                            trans_data[i+3] == 0x04) ||
+                                           (trans_data[i] == 0x00 &&
+                                            trans_data[i+1] == 0x00 &&
+                                            trans_data[i+2] == 0xb0 &&
+                                            trans_data[i+3] == 0x04) ||
+                                           (trans_data[i] == 0x7f &&
+                                            trans_data[i+1] == 0x00 &&
+                                            trans_data[i+2] == 0xb0 &&
+                                            trans_data[i+3] == 0x04)) {
+                                               gunichar2 lang_buf[128];
+                                               guint32 lang, lang_count;
+
+                                               lang = (trans_data[i]) |
+                                                      (trans_data[i+1] << 8) |
+                                                      (trans_data[i+2] << 16) |
+                                                      (trans_data[i+3] << 24);
+                                               lang_count = VerLanguageName (lang, lang_buf, 128);
+                                               if (lang_count) {
+                                                       process_set_field_string (filever, "language", lang_buf, lang_count);
+                                               }
+                                               process_module_stringtable (filever, data, trans_data[i], trans_data[i+1]);
+                                       }
+                               }
+                       }
+                       g_free (query);
                }
+               g_free (data);
        }
 }
 
-static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
+static void process_add_module (GPtrArray *modules, HANDLE process, HMODULE mod,
+                               gunichar2 *filename, gunichar2 *modulename)
 {
        MonoClass *proc_class, *filever_class;
        MonoObject *item, *filever;
        MonoDomain *domain=mono_domain_get ();
-       gchar *modulename;
-       const char* filename;
+       MODULEINFO modinfo;
+       BOOL ok;
        
        /* Build a System.Diagnostics.ProcessModule with the data.
-        * Leave BaseAddress and EntryPointAddress set to NULL,
-        * FileName is ass->image->name, FileVersionInfo is an object
-        * constructed from the PE image data referenced by
-        * ass->image, ModuleMemorySize set to 0, ModuleName the last
-        * component of FileName.
         */
        proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
                                         "ProcessModule");
@@ -597,60 +353,59 @@ static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
                                            "System.Diagnostics",
                                            "FileVersionInfo");
        filever=mono_object_new (domain, filever_class);
-       
-#ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d], ModuleName [%s]", ass->image->name, ass->aname.major, ass->aname.minor, ass->aname.build, ass->aname.revision, ass->image->name);
-#endif
 
-       process_get_fileversion (filever, mono_assembly_get_image (ass));
+       process_get_fileversion (filever, filename);
 
-       filename = mono_image_get_filename (mono_assembly_get_image (ass));
-       process_set_field_string_utf8 (filever, "filename", filename);
-       process_set_field_string_utf8 (item, "filename", filename);
-       process_set_field_object (item, "version_info", filever);
+       process_set_field_string (filever, "filename", filename,
+                                 unicode_chars (filename));
 
-       modulename=g_path_get_basename (filename);
-       process_set_field_string_utf8 (item, "modulename", modulename);
-       g_free (modulename);
+       ok = GetModuleInformation (process, mod, &modinfo, sizeof(MODULEINFO));
+       if (ok) {
+               process_set_field_intptr (item, "baseaddr",
+                                         modinfo.lpBaseOfDll);
+               process_set_field_intptr (item, "entryaddr",
+                                         modinfo.EntryPoint);
+               process_set_field_int (item, "memory_size",
+                                      modinfo.SizeOfImage);
+       }
+       process_set_field_string (item, "filename", filename,
+                                 unicode_chars (filename));
+       process_set_field_string (item, "modulename", modulename,
+                                 unicode_chars (modulename));
+       process_set_field_object (item, "version_info", filever);
 
        /* FIXME: moving GC */
        g_ptr_array_add (modules, item);
 }
 
-static void process_scan_modules (gpointer data, gpointer user_data)
-{
-       MonoAssembly *ass=data;
-       GPtrArray *modules=user_data;
-
-       /* The main assembly is already in the list */
-       if(mono_assembly_get_main () != ass) {
-               process_add_module (modules, ass);
-       }
-}
-
-
 /* Returns an array of System.Diagnostics.ProcessModule */
-MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
+MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process)
 {
-       /* I was going to use toolhelp for this, but then realised I
-        * was being an idiot :)
-        *
-        * (Toolhelp would give shared libraries open by the runtime,
-        * as well as open assemblies.  On windows my tests didnt find
-        * the assemblies loaded by mono either.)
-        */
        GPtrArray *modules_list=g_ptr_array_new ();
        MonoArray *arr;
+       HMODULE mods[1024];
+       gunichar2 filename[MAX_PATH];
+       gunichar2 modname[MAX_PATH];
+       DWORD needed;
+       guint32 count;
        guint32 i;
        
        MONO_ARCH_SAVE_REGS;
 
        STASH_SYS_ASS (this);
-       
-       /* Make sure the first entry is the main module */
-       process_add_module (modules_list, mono_assembly_get_main ());
-       
-       mono_assembly_foreach (process_scan_modules, modules_list);
+
+       if (EnumProcessModules (process, mods, sizeof(mods), &needed)) {
+               count = needed / sizeof(HMODULE);
+               for (i = 0; i < count; i++) {
+                       if (GetModuleBaseName (process, mods[i], modname,
+                                              MAX_PATH) &&
+                           GetModuleFileNameEx (process, mods[i], filename,
+                                                MAX_PATH)) {
+                               process_add_module (modules_list, process,
+                                                   mods[i], filename, modname);
+                       }
+               }
+       }
 
        /* Build a MonoArray out of modules_list */
        arr=mono_array_new (mono_domain_get (), mono_get_object_class (),
@@ -667,30 +422,14 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject
 
 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
 {
-       MonoImage *image;
-       gchar *filename_utf8;
-       
        MONO_ARCH_SAVE_REGS;
 
        STASH_SYS_ASS (this);
        
-       filename_utf8 = mono_string_to_utf8 (filename);
-       image = mono_pe_file_open (filename_utf8, NULL);
-       g_free (filename_utf8);
-       
-       if(image==NULL) {
-               /* FIXME: an exception might be appropriate here */
-#ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
-#endif
-
-               return;
-       }
-       
-       process_get_fileversion (this, image);
-       process_set_field_string_utf8 (this, "filename", mono_image_get_filename (image));
-       
-       mono_image_close (image);
+       process_get_fileversion (this, mono_string_chars (filename));
+       process_set_field_string (this, "filename",
+                                 mono_string_chars (filename),
+                                 mono_string_length (filename));
 }
 
 /* Only used when UseShellExecute is false */
@@ -1048,7 +787,7 @@ gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
 
 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
 {
-       guint32 code;
+       DWORD code;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -1067,7 +806,7 @@ MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE pr
        gboolean ok;
        HMODULE mod;
        gunichar2 name[MAX_PATH];
-       guint32 needed;
+       DWORD needed;
        guint32 len;
        
        MONO_ARCH_SAVE_REGS;
@@ -1097,8 +836,9 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
 {
        MonoArray *procs;
        gboolean ret;
-       guint32 needed, count, i;
-       guint32 pids[1024];
+       DWORD needed;
+       guint32 count, i;
+       DWORD pids[1024];
 
        MONO_ARCH_SAVE_REGS;
 
@@ -1108,7 +848,7 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
                return(NULL);
        }
        
-       count=needed/sizeof(guint32);
+       count=needed/sizeof(DWORD);
        procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
                              count);
        for(i=0; i<count; i++) {
@@ -1121,7 +861,7 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
 {
        gboolean ret;
-       size_t ws_min, ws_max;
+       SIZE_T ws_min, ws_max;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -1135,8 +875,8 @@ MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE
 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
 {
        gboolean ret;
-       size_t ws_min;
-       size_t ws_max;
+       SIZE_T ws_min;
+       SIZE_T ws_max;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -1146,9 +886,9 @@ MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE
        }
        
        if(use_min==TRUE) {
-               ws_min=min;
+               ws_min=(SIZE_T)min;
        } else {
-               ws_max=max;
+               ws_max=(SIZE_T)max;
        }
        
        ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
index 2a609a40be269646f6c925afc4bbfc7b69e63d80..7d3318914e83d8ca8c159840840a78dc9249ebe9 100644 (file)
@@ -61,7 +61,7 @@ HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) MO
 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) MONO_INTERNAL;
 guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void) MONO_INTERNAL;
 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this, HANDLE process) MONO_INTERNAL;
-MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this) MONO_INTERNAL;
+MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process) MONO_INTERNAL;
 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename) MONO_INTERNAL;
 MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_handle) MONO_INTERNAL;
 MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_handle) MONO_INTERNAL;