Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / w32process-unix.c
index 0b387e6d2a48a617e3376fc15bc8acc38f7bd7a1..f71dc34bdb758365d5b4d928476d27255c164a97 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * process.c: System.Diagnostics.Process support
+/**
+ * \file
+ * System.Diagnostics.Process support
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
@@ -49,6 +50,7 @@
 #include <mono/metadata/w32process.h>
 #include <mono/metadata/w32process-internals.h>
 #include <mono/metadata/w32process-unix-internals.h>
+#include <mono/metadata/w32error.h>
 #include <mono/metadata/class.h>
 #include <mono/metadata/class-internals.h>
 #include <mono/metadata/object.h>
@@ -56,8 +58,8 @@
 #include <mono/metadata/metadata.h>
 #include <mono/metadata/metadata-internals.h>
 #include <mono/metadata/exception.h>
-#include <mono/io-layer/io-layer.h>
 #include <mono/metadata/w32handle.h>
+#include <mono/metadata/w32file.h>
 #include <mono/utils/mono-membar.h>
 #include <mono/utils/mono-logger-internals.h>
 #include <mono/utils/strenc.h>
 #include <mono/utils/mono-lazy-init.h>
 #include <mono/utils/mono-signal-handler.h>
 #include <mono/utils/mono-time.h>
+#include <mono/utils/mono-mmap.h>
+#include <mono/utils/strenc.h>
+#include <mono/utils/mono-io-portability.h>
+#include <mono/utils/w32api.h>
 
 #ifndef MAXPATHLEN
 #define MAXPATHLEN 242
@@ -93,18 +99,6 @@ static char *mono_environ[1] = { NULL };
 extern char **environ;
 #endif
 
-/*
- * Handles > _WAPI_PROCESS_UNHANDLED are pseudo handles which represent processes
- * not started by the runtime.
- */
-/* This marks a system process that we don't have a handle on */
-/* FIXME: Cope with PIDs > sizeof guint */
-#define _WAPI_PROCESS_UNHANDLED (1 << (8*sizeof(pid_t)-1))
-
-#define WAPI_IS_PSEUDO_PROCESS_HANDLE(handle) ((GPOINTER_TO_UINT(handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED)
-#define WAPI_PID_TO_HANDLE(pid) GINT_TO_POINTER (_WAPI_PROCESS_UNHANDLED + (pid))
-#define WAPI_HANDLE_TO_PID(handle) (GPOINTER_TO_UINT ((handle)) - _WAPI_PROCESS_UNHANDLED)
-
 typedef enum {
        STARTF_USESHOWWINDOW=0x001,
        STARTF_USESIZE=0x002,
@@ -134,61 +128,409 @@ typedef struct {
 } ProcessTime;
 
 /*
- * MonoProcess describes processes we create.
+ * Process describes processes we create.
  * It contains a semaphore that can be waited on in order to wait
- * for process termination. It's accessed in our SIGCHLD handler,
- * when status is updated (and pid cleared, to not clash with
- * subsequent processes that may get executed).
+ * for process termination.
  */
-typedef struct _MonoProcess MonoProcess;
-struct _MonoProcess {
+typedef struct _Process {
        pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
        MonoSemType exit_sem; /* this semaphore will be released when the process exits */
        int status; /* the exit status */
-       gint32 handle_count; /* the number of handles to this mono_process instance */
+       gint32 handle_count; /* the number of handles to this process instance */
        /* we keep a ref to the creating _WapiHandle_process handle until
         * the process has exited, so that the information there isn't lost.
         */
        gpointer handle;
-       gboolean freeable;
-       MonoProcess *next;
-};
+       gboolean signalled;
+       struct _Process *next;
+} Process;
 
 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
 typedef struct {
-       pid_t id;
+       pid_t pid;
+       gboolean child;
        guint32 exitstatus;
        gpointer main_thread;
-       WapiFileTime create_time;
-       WapiFileTime exit_time;
-       char *proc_name;
+       guint64 create_time;
+       guint64 exit_time;
+       char *pname;
        size_t min_working_set;
        size_t max_working_set;
        gboolean exited;
-       MonoProcess *mono_process;
+       Process *process;
 } MonoW32HandleProcess;
 
+/*
+ * VS_VERSIONINFO:
+ *
+ * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks)
+ * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO")
+ * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary
+ * VS_FIXEDFILEINFO struct
+ * Variable length padding to align Child struct on a 32-bit boundary
+ * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs)
+ */
+
+/*
+ * StringFileInfo:
+ *
+ * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
+ * 2 bytes: Value length (always zero)
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string: Key (currently "StringFileInfo")
+ * Variable length padding to align Child struct on a 32-bit boundary
+ * Child structs ( one or more StringTable structs.  Each StringTable struct's Key member indicates the appropriate language and code page for displaying the text in that StringTable struct.)
+ */
+
+/*
+ * StringTable:
+ *
+ * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks)
+ * 2 bytes: Value length (always zero)
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string: Key. An 8-digit hex number stored as a unicode string.  The four most significant digits represent the language identifier.  The four least significant digits represent the code page for which the data is formatted.
+ * Variable length padding to align Child struct on a 32-bit boundary
+ * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary)
+ */
+
+/*
+ * String:
+ *
+ * 2 bytes: Length in bytes (of this block)
+ * 2 bytes: Value length (the length in words of the Value member)
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string: Key. arbitrary string, identifies data.
+ * Variable length padding to align Value on a 32-bit boundary
+ * Value: Variable length unicode string, holding data.
+ */
+
+/*
+ * VarFileInfo:
+ *
+ * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
+ * 2 bytes: Value length (always zero)
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string: Key (currently "VarFileInfo")
+ * Variable length padding to align Child struct on a 32-bit boundary
+ * Child structs (a Var struct)
+ */
+
+/*
+ * Var:
+ *
+ * 2 bytes: Length in bytes of this block
+ * 2 bytes: Value length in bytes of the Value
+ * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
+ * Variable length unicode string: Key ("Translation")
+ * Variable length padding to align Value on a 32-bit boundary
+ * Value: an array of one or more 4 byte values that are language and code page identifier pairs, low-order word containing a language identifier, and the high-order word containing a code page number.  Either word can be zero, indicating that the file is language or code page independent.
+ */
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define VS_FFI_SIGNATURE       0xbd04effe
+#define VS_FFI_STRUCVERSION    0x00000100
+#else
+#define VS_FFI_SIGNATURE       0xfeef04bd
+#define VS_FFI_STRUCVERSION    0x00010000
+#endif
+
+#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_SIZEOF_SHORT_NAME        8
+
+#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;
+} IMAGE_DOS_HEADER;
+
+typedef struct {
+       guint16 Machine;
+       guint16 NumberOfSections;
+       guint32 TimeDateStamp;
+       guint32 PointerToSymbolTable;
+       guint32 NumberOfSymbols;
+       guint16 SizeOfOptionalHeader;
+       guint16 Characteristics;
+} IMAGE_FILE_HEADER;
+
+typedef struct {
+       guint32 VirtualAddress;
+       guint32 Size;
+} IMAGE_DATA_DIRECTORY;
+
+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;
+       IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER32;
+
+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;
+       IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER64;
+
+#if SIZEOF_VOID_P == 8
+typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
+#else
+typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
+#endif
+
+typedef struct {
+       guint32 Signature;
+       IMAGE_FILE_HEADER FileHeader;
+       IMAGE_OPTIONAL_HEADER32 OptionalHeader;
+} IMAGE_NT_HEADERS32;
+
+typedef struct {
+       guint32 Signature;
+       IMAGE_FILE_HEADER FileHeader;
+       IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} IMAGE_NT_HEADERS64;
+
+#if SIZEOF_VOID_P == 8
+typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
+#else
+typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
+#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;
+} IMAGE_SECTION_HEADER;
+
+#define IMAGE_FIRST_SECTION32(header) ((IMAGE_SECTION_HEADER *)((gsize)(header) + G_STRUCT_OFFSET (IMAGE_NT_HEADERS32, OptionalHeader) + GUINT16_FROM_LE (((IMAGE_NT_HEADERS32 *)(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;
+} IMAGE_RESOURCE_DIRECTORY;
+
+typedef struct {
+       union {
+               struct {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+                       guint32 NameIsString:1;
+                       guint32 NameOffset:31;
+#else
+                       guint32 NameOffset:31;
+                       guint32 NameIsString:1;
+#endif
+               };
+               guint32 Name;
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+               struct {
+                       guint16 __wapi_big_endian_padding;
+                       guint16 Id;
+               };
+#else
+               guint16 Id;
+#endif
+       };
+       union {
+               guint32 OffsetToData;
+               struct {
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+                       guint32 DataIsDirectory:1;
+                       guint32 OffsetToDirectory:31;
+#else
+                       guint32 OffsetToDirectory:31;
+                       guint32 DataIsDirectory:1;
+#endif
+               };
+       };
+} IMAGE_RESOURCE_DIRECTORY_ENTRY;
+
+typedef struct {
+       guint32 OffsetToData;
+       guint32 Size;
+       guint32 CodePage;
+       guint32 Reserved;
+} IMAGE_RESOURCE_DATA_ENTRY;
+
+#define VOS_UNKNOWN            0x00000000
+#define VOS_DOS                        0x00010000
+#define VOS_OS216              0x00020000
+#define VOS_OS232              0x00030000
+#define VOS_NT                 0x00040000
+#define VOS__BASE              0x00000000
+#define VOS__WINDOWS16         0x00000001
+#define VOS__PM16              0x00000002
+#define VOS__PM32              0x00000003
+#define VOS__WINDOWS32         0x00000004
+/* Should "embrace and extend" here with some entries for linux etc */
+
+#define VOS_DOS_WINDOWS16      0x00010001
+#define VOS_DOS_WINDOWS32      0x00010004
+#define VOS_OS216_PM16         0x00020002
+#define VOS_OS232_PM32         0x00030003
+#define VOS_NT_WINDOWS32       0x00040004
+
+#define VFT_UNKNOWN            0x0000
+#define VFT_APP                        0x0001
+#define VFT_DLL                        0x0002
+#define VFT_DRV                        0x0003
+#define VFT_FONT               0x0004
+#define VFT_VXD                        0x0005
+#define VFT_STATIC_LIB         0x0007
+
+#define VFT2_UNKNOWN           0x0000
+#define VFT2_DRV_PRINTER       0x0001
+#define VFT2_DRV_KEYBOARD      0x0002
+#define VFT2_DRV_LANGUAGE      0x0003
+#define VFT2_DRV_DISPLAY       0x0004
+#define VFT2_DRV_MOUSE         0x0005
+#define VFT2_DRV_NETWORK       0x0006
+#define VFT2_DRV_SYSTEM                0x0007
+#define VFT2_DRV_INSTALLABLE   0x0008
+#define VFT2_DRV_SOUND         0x0009
+#define VFT2_DRV_COMM          0x000a
+#define VFT2_DRV_INPUTMETHOD   0x000b
+#define VFT2_FONT_RASTER       0x0001
+#define VFT2_FONT_VECTOR       0x0002
+#define VFT2_FONT_TRUETYPE     0x0003
+
+#define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary)))
+
+#define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
+
 #if HAVE_SIGACTION
 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
 #endif
 
 static gchar *cli_launcher;
 
-/* The signal-safe logic to use processes goes like this:
- * - The list must be safe to traverse for the signal handler at all times.
- *   It's safe to: prepend an entry (which is a single store to 'processes'),
- *   unlink an entry (assuming the unlinked entry isn't freed and doesn't
- *   change its 'next' pointer so that it can still be traversed).
- * When cleaning up we first unlink an entry, then we verify that
- * the read lock isn't locked. Then we can free the entry, since
- * we know that nobody is using the old version of the list (including
- * the unlinked entry).
- * We also need to lock when adding and cleaning up so that those two
- * operations don't mess with eachother. (This lock is not used in the
- * signal handler) */
-static MonoProcess *processes;
+static Process *processes;
 static mono_mutex_t processes_mutex;
 
+static pid_t current_pid;
 static gpointer current_process;
 
 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
@@ -196,12 +538,39 @@ static const gunichar2 *utf16_space = utf16_space_bytes;
 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
 static const gunichar2 *utf16_quote = utf16_quote_bytes;
 
+/* Check if a pid is valid - i.e. if a process exists with this pid. */
+static gboolean
+process_is_alive (pid_t pid)
+{
+#if defined(HOST_WATCHOS)
+       return TRUE; // TODO: Rewrite using sysctl
+#elif defined(HOST_DARWIN) || defined(__OpenBSD__) || defined(__FreeBSD__)
+       if (pid == 0)
+               return FALSE;
+       if (kill (pid, 0) == 0)
+               return TRUE;
+       if (errno == EPERM)
+               return TRUE;
+       return FALSE;
+#elif defined(__HAIKU__)
+       team_info teamInfo;
+       if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
+               return TRUE;
+       return FALSE;
+#else
+       gchar *dir = g_strdup_printf ("/proc/%d", pid);
+       gboolean result = access (dir, F_OK) == 0;
+       g_free (dir);
+       return result;
+#endif
+}
+
 static void
 process_details (gpointer data)
 {
        MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) data;
-       g_print ("id: %d, exited: %s, exitstatus: %d",
-               process_handle->id, process_handle->exited ? "true" : "false", process_handle->exitstatus);
+       g_print ("pid: %d, exited: %s, exitstatus: %d",
+               process_handle->pid, process_handle->exited ? "true" : "false", process_handle->exitstatus);
 }
 
 static const gchar*
@@ -223,14 +592,10 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
        pid_t pid G_GNUC_UNUSED, ret;
        int status;
        gint64 start, now;
-       MonoProcess *mp;
+       Process *process;
        gboolean res;
 
-       /* FIXME: We can now easily wait on processes that aren't our own children,
-        * but WaitFor*Object won't call us for pseudo handles. */
-       g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
-
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT ")", __func__, handle, timeout);
 
        if (alerted)
                *alerted = FALSE;
@@ -243,98 +608,94 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
 
        if (process_handle->exited) {
                /* We've already done this one */
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout);
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Process already exited", __func__, handle, timeout);
                return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
        }
 
-       pid = process_handle->id;
-
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
+       pid = process_handle->pid;
 
-       /* We don't need to lock processes here, the entry
-        * has a handle_count > 0 which means it will not be freed. */
-       mp = process_handle->mono_process;
-       if (!mp) {
-               pid_t res;
+       if (pid == mono_process_current_pid ()) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on current process", __func__, handle, timeout);
+               return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
+       }
 
-               if (pid == mono_process_current_pid ()) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout);
-                       return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
-               }
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): PID: %d", __func__, handle, timeout, pid);
 
-               /* This path is used when calling Process.HasExited, so
-                * it is only used to poll the state of the process, not
-                * to actually wait on it to exit */
-               g_assert (timeout == 0);
+       if (!process_handle->child) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on non-child process", __func__, handle, timeout);
 
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
+               if (!process_is_alive (pid)) {
+                       /* assume the process has exited */
+                       process_handle->exited = TRUE;
+                       process_handle->exitstatus = -1;
+                       mono_w32handle_set_signal_state (handle, TRUE, TRUE);
 
-               res = waitpid (pid, &status, WNOHANG);
-               if (res == 0) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait timeout", __func__, handle, timeout);
-                       return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
-               }
-               if (res > 0) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully", __func__, handle, timeout);
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): non-child process is not alive anymore (2)", __func__, handle, timeout);
                        return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
                }
 
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait failed, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno);
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): non-child process wait failed, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno);
                return MONO_W32HANDLE_WAIT_RET_FAILED;
        }
 
+       /* We don't need to lock processes here, the entry
+        * has a handle_count > 0 which means it will not be freed. */
+       process = process_handle->process;
+       g_assert (process);
+
        start = mono_msec_ticks ();
        now = start;
 
        while (1) {
-               if (timeout != INFINITE) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...",
-                               __func__, handle, timeout, (long)(timeout - (now - start)));
-                       ret = mono_os_sem_timedwait (&mp->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
+               if (timeout != MONO_INFINITE_WAIT) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore for %" G_GINT64_FORMAT " ms...",
+                               __func__, handle, timeout, timeout - (now - start));
+                       ret = mono_os_sem_timedwait (&process->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
                } else {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...",
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore forever...",
                                __func__, handle, timeout);
-                       ret = mono_os_sem_wait (&mp->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
+                       ret = mono_os_sem_wait (&process->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
                }
 
                if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
                        /* Success, process has exited */
-                       mono_os_sem_post (&mp->exit_sem);
+                       mono_os_sem_post (&process->exit_sem);
                        break;
                }
 
                if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout (timeout = 0)", __func__, handle, timeout);
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout (timeout = 0)", __func__, handle, timeout);
                        return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
                }
 
                now = mono_msec_ticks ();
                if (now - start >= timeout) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout", __func__, handle, timeout);
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout", __func__, handle, timeout);
                        return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
                }
 
                if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait alerted", __func__, handle, timeout);
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): wait alerted", __func__, handle, timeout);
                        *alerted = TRUE;
                        return MONO_W32HANDLE_WAIT_RET_ALERTED;
                }
        }
 
        /* Process must have exited */
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Waited successfully", __func__, handle, timeout);
 
-       status = mp->status;
+       status = process->status;
        if (WIFSIGNALED (status))
                process_handle->exitstatus = 128 + WTERMSIG (status);
        else
                process_handle->exitstatus = WEXITSTATUS (status);
-       _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
+
+       process_handle->exit_time = mono_100ns_datetime ();
 
        process_handle->exited = TRUE;
 
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
-                  __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Setting pid %d signalled, exit status %d",
+                  __func__, handle, timeout, process_handle->pid, process_handle->exitstatus);
 
        mono_w32handle_set_signal_state (handle, TRUE, TRUE);
 
@@ -345,11 +706,8 @@ static void
 processes_cleanup (void)
 {
        static gint32 cleaning_up;
-       MonoProcess *mp;
-       MonoProcess *prev = NULL;
-       GSList *finished = NULL;
-       GSList *l;
-       gpointer unref_handle;
+       Process *process;
+       Process *prev = NULL;
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
 
@@ -357,62 +715,39 @@ processes_cleanup (void)
        if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
                return;
 
-       for (mp = processes; mp; mp = mp->next) {
-               if (mp->pid == 0 && mp->handle) {
+       /*
+        * This needs to be done outside the lock but atomically, hence the CAS above.
+        */
+       for (process = processes; process; process = process->next) {
+               if (process->signalled && process->handle) {
                        /* This process has exited and we need to remove the artifical ref
                         * on the handle */
-                       mono_os_mutex_lock (&processes_mutex);
-                       unref_handle = mp->handle;
-                       mp->handle = NULL;
-                       mono_os_mutex_unlock (&processes_mutex);
-                       if (unref_handle)
-                               mono_w32handle_unref (unref_handle);
+                       mono_w32handle_close (process->handle);
+                       process->handle = NULL;
                }
        }
 
-       /*
-        * Remove processes which exited from the processes list.
-        * We need to synchronize with the sigchld handler here, which runs
-        * asynchronously. The handler requires that the processes list
-        * remain valid.
-        */
        mono_os_mutex_lock (&processes_mutex);
 
-       mp = processes;
-       while (mp) {
-               if (mp->handle_count == 0 && mp->freeable) {
+       for (process = processes; process;) {
+               Process *next = process->next;
+               if (process->handle_count == 0 && process->signalled) {
                        /*
                         * Unlink the entry.
-                        * This code can run parallel with the sigchld handler, but the
-                        * modifications it makes are safe.
                         */
-                       if (mp == processes)
-                               processes = mp->next;
+                       if (process == processes)
+                               processes = process->next;
                        else
-                               prev->next = mp->next;
-                       finished = g_slist_prepend (finished, mp);
+                               prev->next = process->next;
 
-                       mp = mp->next;
+                       mono_os_sem_destroy (&process->exit_sem);
+                       g_free (process);
                } else {
-                       prev = mp;
-                       mp = mp->next;
+                       prev = process;
                }
+               process = next;
        }
 
-       mono_memory_barrier ();
-
-       for (l = finished; l; l = l->next) {
-               /*
-                * All the entries in the finished list are unlinked from processes, and
-                * they have the 'finished' flag set, which means the sigchld handler is done
-                * accessing them.
-                */
-               mp = (MonoProcess *)l->data;
-               mono_os_sem_destroy (&mp->exit_sem);
-               g_free (mp);
-       }
-       g_slist_free (finished);
-
        mono_os_mutex_unlock (&processes_mutex);
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
@@ -428,10 +763,10 @@ process_close (gpointer handle, gpointer data)
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
 
        process_handle = (MonoW32HandleProcess *) data;
-       g_free (process_handle->proc_name);
-       process_handle->proc_name = NULL;
-       if (process_handle->mono_process)
-               InterlockedDecrement (&process_handle->mono_process->handle_count);
+       g_free (process_handle->pname);
+       process_handle->pname = NULL;
+       if (process_handle->process)
+               InterlockedDecrement (&process_handle->process->handle_count);
        processes_cleanup ();
 }
 
@@ -454,7 +789,7 @@ process_set_defaults (MonoW32HandleProcess *process_handle)
        process_handle->min_working_set = 204800;
        process_handle->max_working_set = 1413120;
 
-       _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
+       process_handle->create_time = mono_100ns_datetime ();
 }
 
 static void
@@ -470,9 +805,9 @@ process_set_name (MonoW32HandleProcess *process_handle)
        if (utf8_progname) {
                slash = strrchr (utf8_progname, '/');
                if (slash)
-                       process_handle->proc_name = g_strdup (slash+1);
+                       process_handle->pname = g_strdup (slash+1);
                else
-                       process_handle->proc_name = g_strdup (utf8_progname);
+                       process_handle->pname = g_strdup (utf8_progname);
                g_free (utf8_progname);
        }
 }
@@ -487,13 +822,15 @@ mono_w32process_init (void)
        mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
                (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
 
+       current_pid = getpid ();
+
        memset (&process_handle, 0, sizeof (process_handle));
-       process_handle.id = wapi_getpid ();
+       process_handle.pid = current_pid;
        process_set_defaults (&process_handle);
        process_set_name (&process_handle);
 
        current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
-       g_assert (current_process);
+       g_assert (current_process != INVALID_HANDLE_VALUE);
 
        mono_os_mutex_init (&processes_mutex);
 }
@@ -504,31 +841,6 @@ mono_w32process_cleanup (void)
        g_free (cli_launcher);
 }
 
-/* Check if a pid is valid - i.e. if a process exists with this pid. */
-static gboolean
-is_pid_valid (pid_t pid)
-{
-       gboolean result = FALSE;
-
-#if defined(HOST_WATCHOS)
-       result = TRUE; // TODO: Rewrite using sysctl
-#elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
-       if (((kill(pid, 0) == 0) || (errno == EPERM)) && pid != 0)
-               result = TRUE;
-#elif defined(__HAIKU__)
-       team_info teamInfo;
-       if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
-               result = TRUE;
-#else
-       char *dir = g_strdup_printf ("/proc/%d", pid);
-       if (!access (dir, F_OK))
-               result = TRUE;
-       g_free (dir);
-#endif
-
-       return result;
-}
-
 static int
 len16 (const gunichar2 *str)
 {
@@ -579,18 +891,13 @@ mono_w32process_get_pid (gpointer handle)
        MonoW32HandleProcess *process_handle;
        gboolean res;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
-               /* This is a pseudo handle */
-               return WAPI_HANDLE_TO_PID (handle);
-       }
-
        res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
        if (!res) {
-               SetLastError (ERROR_INVALID_HANDLE);
+               mono_w32error_set_last (ERROR_INVALID_HANDLE);
                return 0;
        }
 
-       return process_handle->id;
+       return process_handle->pid;
 }
 
 typedef struct {
@@ -610,13 +917,11 @@ get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointe
        if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS)
                return FALSE;
 
-       g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
-
        process_handle = (MonoW32HandleProcess*) handle_specific;
 
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->pid);
 
-       pid = process_handle->id;
+       pid = process_handle->pid;
        if (pid == 0)
                return FALSE;
 
@@ -628,8 +933,7 @@ get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointe
        if (mono_w32handle_issignalled (handle))
                return FALSE;
 
-       mono_w32handle_ref (handle);
-       foreach_data->handle = handle;
+       foreach_data->handle = mono_w32handle_duplicate (handle);
        return TRUE;
 }
 
@@ -650,14 +954,28 @@ ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
                return handle;
        }
 
-       if (is_pid_valid (pid)) {
-               /* Return a pseudo handle for processes we don't have handles for */
-               return WAPI_PID_TO_HANDLE (pid);
+       if (process_is_alive (pid)) {
+               /* non-child process */
+               MonoW32HandleProcess process_handle;
+
+               memset (&process_handle, 0, sizeof (process_handle));
+               process_handle.pid = pid;
+               process_handle.pname = mono_w32process_get_name (pid);
+
+               handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
+               if (handle == INVALID_HANDLE_VALUE) {
+                       g_warning ("%s: error creating process handle", __func__);
+
+                       mono_w32error_set_last (ERROR_OUTOFMEMORY);
+                       return NULL;
+               }
+
+               return handle;
        }
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
 
-       SetLastError (ERROR_PROC_NOT_FOUND);
+       mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
        return NULL;
 }
 
@@ -702,7 +1020,7 @@ match_procname_to_modulename (char *procname, char *modulename)
        g_free (pname);
        g_free (mname);
 
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %" G_GINT32_FORMAT, __func__, result);
        return result;
 }
 
@@ -715,7 +1033,7 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si
        guint32 count, avail = size / sizeof(gpointer);
        int i;
        pid_t pid;
-       char *proc_name = NULL;
+       char *pname = NULL;
        gboolean res;
 
        /* Store modules in an array of pointers (main module as
@@ -730,21 +1048,16 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si
        if (size < sizeof(gpointer))
                return FALSE;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
-               pid = WAPI_HANDLE_TO_PID (process);
-               proc_name = mono_w32process_get_name (pid);
-       } else {
-               res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
-                       return FALSE;
-               }
-
-               pid = process_handle->id;
-               proc_name = g_strdup (process_handle->proc_name);
+       res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
+               return FALSE;
        }
 
-       if (!proc_name) {
+       pid = process_handle->pid;
+       pname = g_strdup (process_handle->pname);
+
+       if (!pname) {
                modules[0] = NULL;
                *needed = sizeof(gpointer);
                return TRUE;
@@ -754,7 +1067,7 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si
        if (!mods) {
                modules[0] = NULL;
                *needed = sizeof(gpointer);
-               g_free (proc_name);
+               g_free (pname);
                return TRUE;
        }
 
@@ -775,7 +1088,7 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si
                        module = (MonoW32ProcessModule *)mods_iter->data;
                        if (modules[0] != NULL)
                                modules[i] = module->address_start;
-                       else if (match_procname_to_modulename (proc_name, module->filename))
+                       else if (match_procname_to_modulename (pname, module->filename))
                                modules[0] = module->address_start;
                        else
                                modules[i + 1] = module->address_start;
@@ -789,7 +1102,7 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si
        *needed = sizeof(gpointer) * (count + 1);
 
        g_slist_free (mods);
-       g_free (proc_name);
+       g_free (pname);
 
        return TRUE;
 }
@@ -825,10 +1138,10 @@ mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar
        bytes += 2;
 
        if (size < bytes) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes);
                memcpy (basename, proc_path, size);
        } else {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)", __func__, size, bytes);
                memcpy (basename, proc_path, bytes);
        }
 
@@ -848,35 +1161,30 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
        gsize bytes;
        GSList *mods = NULL, *mods_iter;
        MonoW32ProcessModule *found_module;
-       char *proc_name = NULL;
+       char *pname = NULL;
        gboolean res;
 
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
-                  __func__, process, module);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p basename %p size %" G_GUINT32_FORMAT,
+                  __func__, process, module, basename, size);
 
        size = size * sizeof (gunichar2); /* adjust for unicode characters */
 
        if (basename == NULL || size == 0)
                return 0;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
-               /* This is a pseudo handle */
-               pid = (pid_t)WAPI_HANDLE_TO_PID (process);
-               proc_name = mono_w32process_get_name (pid);
-       } else {
-               res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
-                       return 0;
-               }
-
-               pid = process_handle->id;
-               proc_name = g_strdup (process_handle->proc_name);
+       res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
+               return 0;
        }
 
+       pid = process_handle->pid;
+       pname = g_strdup (process_handle->pname);
+
        mods = mono_w32process_get_modules (pid);
-       if (!mods) {
-               g_free (proc_name);
+       if (!mods && module != NULL) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't get modules %p", __func__, process);
+               g_free (pname);
                return 0;
        }
 
@@ -887,7 +1195,7 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
        for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
                found_module = (MonoW32ProcessModule *)mods_iter->data;
                if (procname_ext == NULL &&
-                       ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+                       ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) ||
                         (module != NULL && found_module->address_start == module))) {
                        procname_ext = g_path_get_basename (found_module->filename);
                }
@@ -896,15 +1204,18 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
        }
 
        if (procname_ext == NULL) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext from procmods %p", __func__, process);
                /* If it's *still* null, we might have hit the
                 * case where reading /proc/$pid/maps gives an
                 * empty file for this user.
                 */
                procname_ext = mono_w32process_get_name (pid);
+               if (!procname_ext)
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__, process, pid);
        }
 
        g_slist_free (mods);
-       g_free (proc_name);
+       g_free (pname);
 
        if (procname_ext) {
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
@@ -912,6 +1223,7 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
 
                procname = mono_unicode_from_external (procname_ext, &bytes);
                if (procname == NULL) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't get procname %p", __func__, process);
                        /* bugger */
                        g_free (procname_ext);
                        return 0;
@@ -923,11 +1235,11 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
                bytes += 2;
 
                if (size < bytes) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes);
 
                        memcpy (basename, procname, size);
                } else {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)",
                                   __func__, size, bytes);
 
                        memcpy (basename, procname, bytes);
@@ -939,43 +1251,39 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b
                return len;
        }
 
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext %p", __func__, process);
        return 0;
 }
 
 gboolean
-mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
+mono_w32process_module_get_information (gpointer process, gpointer module, MODULEINFO *modinfo, guint32 size)
 {
        MonoW32HandleProcess *process_handle;
        pid_t pid;
        GSList *mods = NULL, *mods_iter;
        MonoW32ProcessModule *found_module;
        gboolean ret = FALSE;
-       char *proc_name = NULL;
+       char *pname = NULL;
        gboolean res;
 
        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
                   __func__, process, module);
 
-       if (modinfo == NULL || size < sizeof (WapiModuleInfo))
+       if (modinfo == NULL || size < sizeof (MODULEINFO))
                return FALSE;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
-               pid = (pid_t)WAPI_HANDLE_TO_PID (process);
-               proc_name = mono_w32process_get_name (pid);
-       } else {
-               res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
-                       return FALSE;
-               }
-
-               pid = process_handle->id;
-               proc_name = g_strdup (process_handle->proc_name);
+       res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
+               return FALSE;
        }
 
+       pid = process_handle->pid;
+       pname = g_strdup (process_handle->pname);
+
        mods = mono_w32process_get_modules (pid);
        if (!mods) {
-               g_free (proc_name);
+               g_free (pname);
                return FALSE;
        }
 
@@ -986,7 +1294,7 @@ mono_w32process_module_get_information (gpointer process, gpointer module, WapiM
        for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
                        found_module = (MonoW32ProcessModule *)mods_iter->data;
                        if (ret == FALSE &&
-                               ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+                               ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) ||
                                 (module != NULL && found_module->address_start == module))) {
                                modinfo->lpBaseOfDll = found_module->address_start;
                                modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
@@ -998,7 +1306,7 @@ mono_w32process_module_get_information (gpointer process, gpointer module, WapiM
        }
 
        g_slist_free (mods);
-       g_free (proc_name);
+       g_free (pname);
 
        return ret;
 }
@@ -1019,10 +1327,43 @@ switch_dir_separators (char *path)
 #if HAVE_SIGACTION
 
 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
+{
+       /*
+        * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
+        * mono_w32process_signal_finished ().
+        */
+       int old_errno = errno;
+
+       mono_gc_finalize_notify ();
+
+       errno = old_errno;
+}
+
+static void
+process_add_sigchld_handler (void)
+{
+       struct sigaction sa;
+
+       sa.sa_sigaction = mono_sigchld_signal_handler;
+       sigemptyset (&sa.sa_mask);
+       sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
+       g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
+       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+}
+
+#endif
+
+/*
+ * mono_w32process_signal_finished:
+ *
+ *   Signal the exit semaphore for processes which have finished.
+ */
+void
+mono_w32process_signal_finished (void)
 {
        int status;
        int pid;
-       MonoProcess *p;
+       Process *process;
 
        do {
                do {
@@ -1032,38 +1373,24 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi
                if (pid <= 0)
                        break;
 
-               /*
-                * This can run concurrently with the code in the rest of this module.
-                */
-               for (p = processes; p; p = p->next) {
-                       if (p->pid != pid)
+               mono_os_mutex_lock (&processes_mutex);
+
+               for (process = processes; process; process = process->next) {
+                       if (process->pid != pid)
+                               continue;
+                       if (process->signalled)
                                continue;
 
-                       p->pid = 0; /* this pid doesn't exist anymore, clear it */
-                       p->status = status;
-                       mono_os_sem_post (&p->exit_sem);
-                       mono_memory_barrier ();
-                       /* Mark this as freeable, the pointer becomes invalid afterwards */
-                       p->freeable = TRUE;
+                       process->signalled = TRUE;
+                       process->status = status;
+                       mono_os_sem_post (&process->exit_sem);
                        break;
                }
-       } while (1);
-}
-
-static void
-process_add_sigchld_handler (void)
-{
-       struct sigaction sa;
 
-       sa.sa_sigaction = mono_sigchld_signal_handler;
-       sigemptyset (&sa.sa_mask);
-       sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
-       g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
-       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+               mono_os_mutex_unlock (&processes_mutex);
+       } while (1);
 }
 
-#endif
-
 static gboolean
 is_readable_or_executable (const char *prog)
 {
@@ -1222,13 +1549,13 @@ leave:
 }
 
 static gboolean
-process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new_environ,
+process_create (const gunichar2 *appname, const gunichar2 *cmdline,
        const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
 {
 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
        char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
        char *dir = NULL, **env_strings = NULL, **argv = NULL;
-       guint32 i, env_count = 0;
+       guint32 i;
        gboolean ret = FALSE;
        gpointer handle = NULL;
        GError *gerr = NULL;
@@ -1236,7 +1563,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
        pid_t pid = 0;
        int startup_pipe [2] = {-1, -1};
        int dummy;
-       MonoProcess *mono_process;
+       Process *process;
 
 #if HAVE_SIGACTION
        mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
@@ -1274,7 +1601,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
                                   __func__);
 
-                       SetLastError (ERROR_PATH_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
                        goto free_strings;
                }
 
@@ -1286,7 +1613,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                if (args == NULL) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
 
-                       SetLastError (ERROR_PATH_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
                        goto free_strings;
                }
        }
@@ -1296,7 +1623,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                if (dir == NULL) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
 
-                       SetLastError (ERROR_PATH_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
                        goto free_strings;
                }
 
@@ -1327,7 +1654,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
                                           __func__, prog);
                                g_free (unquoted);
-                               SetLastError (ERROR_FILE_NOT_FOUND);
+                               mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
                                goto free_strings;
                        }
                } else {
@@ -1344,7 +1671,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
                                           __func__, prog);
                                g_free (unquoted);
-                               SetLastError (ERROR_FILE_NOT_FOUND);
+                               mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
                                goto free_strings;
                        }
                }
@@ -1408,7 +1735,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                        /* Give up */
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
 
-                       SetLastError (ERROR_PATH_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
                        goto free_strings;
                }
 
@@ -1435,7 +1762,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
                                           __func__, token);
                                g_free (token);
-                               SetLastError (ERROR_FILE_NOT_FOUND);
+                               mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
                                goto free_strings;
                        }
                } else {
@@ -1462,7 +1789,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
 
                                        g_free (token);
-                                       SetLastError (ERROR_FILE_NOT_FOUND);
+                                       mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
                                        goto free_strings;
                                }
                        }
@@ -1491,7 +1818,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                        g_free (newapp);
 
                        if (newcmd) {
-                               ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
+                               ret = process_create (NULL, newcmd, cwd, startup_handles, process_info);
 
                                g_free (newcmd);
 
@@ -1501,7 +1828,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
        } else {
                if (!is_executable (prog)) {
                        mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
-                       SetLastError (ERROR_ACCESS_DENIED);
+                       mono_w32error_set_last (ERROR_ACCESS_DENIED);
                        goto free_strings;
                }
        }
@@ -1529,64 +1856,61 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                out_fd = GPOINTER_TO_UINT (startup_handles->output);
                err_fd = GPOINTER_TO_UINT (startup_handles->error);
        } else {
-               in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
-               out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
-               err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
+               in_fd = GPOINTER_TO_UINT (mono_w32file_get_console_input ());
+               out_fd = GPOINTER_TO_UINT (mono_w32file_get_console_output ());
+               err_fd = GPOINTER_TO_UINT (mono_w32file_get_console_error ());
        }
 
-       /* new_environ is a block of NULL-terminated strings, which
-        * is itself NULL-terminated. Of course, passing an array of
-        * string pointers would have made things too easy :-(
+       /*
+        * process->env_variables is a an array of MonoString*
         *
         * If new_environ is not NULL it specifies the entire set of
         * environment variables in the new process.  Otherwise the
         * new process inherits the same environment.
         */
-       if (new_environ) {
-               gunichar2 *new_environp;
+       if (process_info->env_variables) {
+               gint i, str_length, var_length;
+               MonoString *var;
+               gunichar2 *str;
 
-               /* Count the number of strings */
-               for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
-                       env_count++;
-                       while (*new_environp)
-                               new_environp++;
-               }
+               /* +2: one for the process handle value, and the last one is NULL */
+               env_strings = g_new0 (gchar*, mono_array_length (process_info->env_variables) + 2);
 
-               /* +2: one for the process handle value, and the last
-                * one is NULL
-                */
-               env_strings = g_new0 (char *, env_count + 2);
+               str = NULL;
+               str_length = 0;
 
-               /* Copy each environ string into 'strings' turning it
-                * into utf8 (or the requested encoding) at the same
-                * time
-                */
-               env_count = 0;
-               for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
-                       env_strings[env_count] = mono_unicode_to_external (new_environp);
-                       env_count++;
-                       while (*new_environp) {
-                               new_environp++;
+               /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
+               for (i = 0; i < mono_array_length (process_info->env_variables); ++i) {
+                       var = mono_array_get (process_info->env_variables, MonoString*, i);
+                       var_length = mono_string_length (var);
+
+                       /* str is a null-terminated copy of var */
+
+                       if (var_length + 1 > str_length) {
+                               str_length = var_length + 1;
+                               str = g_renew (gunichar2, str, str_length);
                        }
+
+                       memcpy (str, mono_string_chars (var), var_length * sizeof (gunichar2));
+                       str [var_length] = '\0';
+
+                       env_strings [i] = mono_unicode_to_external (str);
                }
+
+               g_free (str);
        } else {
+               guint32 env_count;
+
+               env_count = 0;
                for (i = 0; environ[i] != NULL; i++)
                        env_count++;
 
-               /* +2: one for the process handle value, and the last
-                * one is NULL
-                */
-               env_strings = g_new0 (char *, env_count + 2);
+               /* +2: one for the process handle value, and the last one is NULL */
+               env_strings = g_new0 (gchar*, env_count + 2);
 
-               /* Copy each environ string into 'strings' turning it
-                * into utf8 (or the requested encoding) at the same
-                * time
-                */
-               env_count = 0;
-               for (i = 0; environ[i] != NULL; i++) {
-                       env_strings[env_count] = g_strdup (environ[i]);
-                       env_count++;
-               }
+               /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
+               for (i = 0; i < env_count; i++)
+                       env_strings [i] = g_strdup (environ[i]);
        }
 
        /* Create a pipe to make sure the child doesn't exit before
@@ -1598,13 +1922,9 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__);
        }
 
-#if HAVE_SIGACTION
-       /* FIXME: block SIGCHLD */
-#endif
-
        switch (pid = fork ()) {
        case -1: /* Error */ {
-               SetLastError (ERROR_OUTOFMEMORY);
+               mono_w32error_set_last (ERROR_OUTOFMEMORY);
                ret = FALSE;
                break;
        }
@@ -1625,17 +1945,17 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                dup2 (err_fd, 2);
 
                /* Close all file descriptors */
-               for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
+               for (i = eg_getdtablesize() - 1; i > 2; i--)
                        close (i);
 
 #ifdef DEBUG_ENABLED
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
                           dir == NULL?".":dir);
                for (i = 0; argv[i] != NULL; i++)
-                       g_message ("arg %d: [%s]", i, argv[i]);
+                       g_message ("arg %" G_GUINT32_FORMAT ": [%s]", i, argv[i]);
 
                for (i = 0; env_strings[i] != NULL; i++)
-                       g_message ("env %d: [%s]", i, env_strings[i]);
+                       g_message ("env %" G_GUINT32_FORMAT ": [%s]", i, env_strings[i]);
 #endif
 
                /* set cwd */
@@ -1656,38 +1976,39 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
                MonoW32HandleProcess process_handle;
 
                memset (&process_handle, 0, sizeof (process_handle));
-               process_handle.id = pid;
-               process_handle.proc_name = g_strdup (prog);
+               process_handle.pid = pid;
+               process_handle.child = TRUE;
+               process_handle.pname = g_strdup (prog);
                process_set_defaults (&process_handle);
 
-               /* Add our mono_process into the linked list of processes */
-               mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess));
-               mono_process->pid = pid;
-               mono_process->handle_count = 1;
-               mono_os_sem_init (&mono_process->exit_sem, 0);
+               /* Add our process into the linked list of processes */
+               process = (Process *) g_malloc0 (sizeof (Process));
+               process->pid = pid;
+               process->handle_count = 1;
+               mono_os_sem_init (&process->exit_sem, 0);
 
-               process_handle.mono_process = mono_process;
+               process_handle.process = process;
 
                handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
                if (handle == INVALID_HANDLE_VALUE) {
                        g_warning ("%s: error creating process handle", __func__);
 
-                       mono_os_sem_destroy (&mono_process->exit_sem);
-                       g_free (mono_process);
+                       mono_os_sem_destroy (&process->exit_sem);
+                       g_free (process);
 
-                       SetLastError (ERROR_OUTOFMEMORY);
+                       mono_w32error_set_last (ERROR_OUTOFMEMORY);
                        ret = FALSE;
                        break;
                }
 
                /* Keep the process handle artificially alive until the process
                 * exits so that the information in the handle isn't lost. */
-               mono_w32handle_ref (handle);
-               mono_process->handle = handle;
+               process->handle = mono_w32handle_duplicate (handle);
 
                mono_os_mutex_lock (&processes_mutex);
-               mono_process->next = processes;
-               processes = mono_process;
+               process->next = processes;
+               mono_memory_barrier ();
+               processes = process;
                mono_os_mutex_unlock (&processes_mutex);
 
                if (process_info != NULL) {
@@ -1703,10 +2024,6 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new
        }
        }
 
-#if HAVE_SIGACTION
-       /* FIXME: unblock SIGCHLD */
-#endif
-
        if (startup_pipe [1] != -1) {
                /* Write 1 byte, doesn't matter what */
                ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
@@ -1737,7 +2054,7 @@ free_strings:
 
        return ret;
 #else
-       SetLastError (ERROR_NOT_SUPPORTED);
+       mono_w32error_set_last (ERROR_NOT_SUPPORTED);
        return FALSE;
 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
 }
@@ -1769,14 +2086,14 @@ ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStar
         */
        args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
        if (args == NULL) {
-               SetLastError (ERROR_INVALID_DATA);
+               mono_w32error_set_last (ERROR_INVALID_DATA);
                ret = FALSE;
                goto done;
        }
-       ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
+       ret = process_create (NULL, args, lpDirectory, NULL, process_info);
        g_free (args);
 
-       if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
+       if (!ret && mono_w32error_get_last () == ERROR_OUTOFMEMORY)
                goto done;
 
        if (!ret) {
@@ -1788,7 +2105,7 @@ ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStar
                        goto done;
                }
 
-#ifdef PLATFORM_MACOSX
+#ifdef HOST_DARWIN
                handler = g_strdup ("/usr/bin/open");
 #else
                /*
@@ -1826,26 +2143,26 @@ ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStar
                args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
                        lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
                if (args == NULL) {
-                       SetLastError (ERROR_INVALID_DATA);
+                       mono_w32error_set_last (ERROR_INVALID_DATA);
                        ret = FALSE;
                        goto done;
                }
-               ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
+               ret = process_create (NULL, args, lpDirectory, NULL, process_info);
                g_free (args);
                if (!ret) {
-                       if (GetLastError () != ERROR_OUTOFMEMORY)
-                               SetLastError (ERROR_INVALID_DATA);
+                       if (mono_w32error_get_last () != ERROR_OUTOFMEMORY)
+                               mono_w32error_set_last (ERROR_INVALID_DATA);
                        ret = FALSE;
                        goto done;
                }
                /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
-               CloseHandle (process_info->process_handle);
+               mono_w32handle_close (process_info->process_handle);
                process_info->process_handle = NULL;
        }
 
 done:
        if (ret == FALSE) {
-               process_info->pid = -GetLastError ();
+               process_info->pid = -mono_w32error_get_last ();
        } else {
                process_info->thread_handle = NULL;
 #if !defined(MONO_CROSS_COMPILE)
@@ -1894,20 +2211,18 @@ process_get_complete_path (const gunichar2 *appname, gchar **completed)
 }
 
 static gboolean
-process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
+process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path)
 {
-       gchar *spath = NULL;
+       gchar *complete_path = NULL;
 
        *shell_path = NULL;
-       *cmd = proc_start_info->arguments;
 
-       process_get_complete_path (mono_string_chars (proc_start_info->filename), &spath);
-       if (spath != NULL) {
-               *shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
-               g_free (spath);
+       if (process_get_complete_path (mono_string_chars (proc_start_info->filename), &complete_path)) {
+               *shell_path = g_utf8_to_utf16 (complete_path, -1, NULL, NULL, NULL);
+               g_free (complete_path);
        }
 
-       return (*shell_path != NULL) ? TRUE : FALSE;
+       return *shell_path != NULL;
 }
 
 MonoBoolean
@@ -1918,72 +2233,32 @@ ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStart
        gunichar2 *dir;
        StartupHandles startup_handles;
        gunichar2 *shell_path = NULL;
-       gchar *env_vars = NULL;
-       MonoString *cmd = NULL;
+       gunichar2 *args = NULL;
 
        memset (&startup_handles, 0, sizeof (startup_handles));
        startup_handles.input = stdin_handle;
        startup_handles.output = stdout_handle;
        startup_handles.error = stderr_handle;
 
-       if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
+       if (!process_get_shell_arguments (proc_start_info, &shell_path)) {
                process_info->pid = -ERROR_FILE_NOT_FOUND;
                return FALSE;
        }
 
-       if (process_info->env_keys) {
-               gint i, len; 
-               MonoString *ms;
-               MonoString *key, *value;
-               gunichar2 *str, *ptr;
-               gunichar2 *equals16;
+       args = proc_start_info->arguments && mono_string_length (proc_start_info->arguments) > 0 ?
+                       mono_string_chars (proc_start_info->arguments): NULL;
 
-               for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
-                       ms = mono_array_get (process_info->env_values, MonoString *, i);
-                       if (ms == NULL)
-                               continue;
+       /* The default dir name is "".  Turn that into NULL to mean "current directory" */
+       dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
+                       mono_string_chars (proc_start_info->working_directory) : NULL;
 
-                       len += mono_string_length (ms) * sizeof (gunichar2);
-                       ms = mono_array_get (process_info->env_keys, MonoString *, i);
-                       len += mono_string_length (ms) * sizeof (gunichar2);
-                       len += 2 * sizeof (gunichar2);
-               }
+       ret = process_create (shell_path, args, dir, &startup_handles, process_info);
 
-               equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
-               ptr = str = g_new0 (gunichar2, len + 1);
-               for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
-                       value = mono_array_get (process_info->env_values, MonoString *, i);
-                       if (value == NULL)
-                               continue;
+       if (shell_path != NULL)
+               g_free (shell_path);
 
-                       key = mono_array_get (process_info->env_keys, MonoString *, i);
-                       memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
-                       ptr += mono_string_length (key);
-
-                       memcpy (ptr, equals16, sizeof (gunichar2));
-                       ptr++;
-
-                       memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
-                       ptr += mono_string_length (value);
-                       ptr++;
-               }
-
-               g_free (equals16);
-               env_vars = (gchar *) str;
-       }
-       
-       /* The default dir name is "".  Turn that into NULL to mean "current directory" */
-       dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
-                       mono_string_chars (proc_start_info->working_directory) : NULL;
-
-       ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
-
-       g_free (env_vars);
-       if (shell_path != NULL)
-               g_free (shell_path);
-
-       if (!ret)
-               process_info->pid = -GetLastError ();
+       if (!ret)
+               process_info->pid = -mono_w32error_get_last ();
 
        return ret;
 }
@@ -2028,7 +2303,6 @@ mono_w32process_set_cli_launcher (gchar *path)
 gpointer
 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
 {
-       mono_w32handle_ref (current_process);
        return current_process;
 }
 
@@ -2036,32 +2310,18 @@ MonoBoolean
 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
 {
        MonoW32HandleProcess *process_handle;
-       guint32 pid;
        gboolean res;
 
        if (!exitcode)
                return FALSE;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
-               pid = WAPI_HANDLE_TO_PID (handle);
-               /* This is a pseudo handle, so we don't know what the exit
-                * code was, but we can check whether it's alive or not */
-               if (is_pid_valid (pid)) {
-                       *exitcode = STILL_ACTIVE;
-                       return TRUE;
-               } else {
-                       *exitcode = -1;
-                       return TRUE;
-               }
-       }
-
        res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
        if (!res) {
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
                return FALSE;
        }
 
-       if (process_handle->id == wapi_getpid ()) {
+       if (process_handle->pid == current_pid) {
                *exitcode = STILL_ACTIVE;
                return TRUE;
        }
@@ -2079,9 +2339,7 @@ ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gin
 MonoBoolean
 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
 {
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
-               return TRUE;
-       return CloseHandle (handle);
+       return mono_w32handle_close (handle);
 }
 
 MonoBoolean
@@ -2091,32 +2349,26 @@ ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint3
        MonoW32HandleProcess *process_handle;
        int ret;
        pid_t pid;
+       gboolean res;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
-               /* This is a pseudo handle */
-               pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
-       } else {
-               gboolean res;
-
-               res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
-                       SetLastError (ERROR_INVALID_HANDLE);
-                       return FALSE;
-               }
-
-               pid = process_handle->id;
+       res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
+               mono_w32error_set_last (ERROR_INVALID_HANDLE);
+               return FALSE;
        }
 
+       pid = process_handle->pid;
+
        ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
        if (ret == 0)
                return TRUE;
 
        switch (errno) {
-       case EINVAL: SetLastError (ERROR_INVALID_PARAMETER); break;
-       case EPERM:  SetLastError (ERROR_ACCESS_DENIED);     break;
-       case ESRCH:  SetLastError (ERROR_PROC_NOT_FOUND);    break;
-       default:     SetLastError (ERROR_GEN_FAILURE);       break;
+       case EINVAL: mono_w32error_set_last (ERROR_INVALID_PARAMETER); break;
+       case EPERM:  mono_w32error_set_last (ERROR_ACCESS_DENIED);     break;
+       case ESRCH:  mono_w32error_set_last (ERROR_PROC_NOT_FOUND);    break;
+       default:     mono_w32error_set_last (ERROR_GEN_FAILURE);       break;
        }
 
        return FALSE;
@@ -2134,15 +2386,15 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handl
        if (!min || !max)
                return FALSE;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
-               return FALSE;
-
        res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
        if (!res) {
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
                return FALSE;
        }
 
+       if (!process_handle->child)
+               return FALSE;
+
        *min = process_handle->min_working_set;
        *max = process_handle->max_working_set;
        return TRUE;
@@ -2154,15 +2406,15 @@ ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handl
        MonoW32HandleProcess *process_handle;
        gboolean res;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
-               return FALSE;
-
        res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
        if (!res) {
                mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
                return FALSE;
        }
 
+       if (!process_handle->child)
+               return FALSE;
+
        process_handle->min_working_set = min;
        process_handle->max_working_set = max;
        return TRUE;
@@ -2175,35 +2427,29 @@ ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
        MonoW32HandleProcess *process_handle;
        gint ret;
        pid_t pid;
+       gboolean res;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
-               /* This is a pseudo handle */
-               pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
-       } else {
-               gboolean res;
-
-               res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       SetLastError (ERROR_INVALID_HANDLE);
-                       return 0;
-               }
-
-               pid = process_handle->id;
+       res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_w32error_set_last (ERROR_INVALID_HANDLE);
+               return 0;
        }
 
+       pid = process_handle->pid;
+
        errno = 0;
        ret = getpriority (PRIO_PROCESS, pid);
        if (ret == -1 && errno != 0) {
                switch (errno) {
                case EPERM:
                case EACCES:
-                       SetLastError (ERROR_ACCESS_DENIED);
+                       mono_w32error_set_last (ERROR_ACCESS_DENIED);
                        break;
                case ESRCH:
-                       SetLastError (ERROR_PROC_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
                        break;
                default:
-                       SetLastError (ERROR_GEN_FAILURE);
+                       mono_w32error_set_last (ERROR_GEN_FAILURE);
                }
                return 0;
        }
@@ -2223,7 +2469,7 @@ ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
 
        return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
 #else
-       SetLastError (ERROR_NOT_SUPPORTED);
+       mono_w32error_set_last (ERROR_NOT_SUPPORTED);
        return 0;
 #endif
 }
@@ -2236,22 +2482,16 @@ ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint3
        int ret;
        int prio;
        pid_t pid;
+       gboolean res;
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
-               /* This is a pseudo handle */
-               pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
-       } else {
-               gboolean res;
-
-               res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-               if (!res) {
-                       SetLastError (ERROR_INVALID_HANDLE);
-                       return FALSE;
-               }
-
-               pid = process_handle->id;
+       res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_w32error_set_last (ERROR_INVALID_HANDLE);
+               return FALSE;
        }
 
+       pid = process_handle->pid;
+
        switch (priorityClass) {
        case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
                prio = 19;
@@ -2272,7 +2512,7 @@ ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint3
                prio = -20;
                break;
        default:
-               SetLastError (ERROR_INVALID_PARAMETER);
+               mono_w32error_set_last (ERROR_INVALID_PARAMETER);
                return FALSE;
        }
 
@@ -2281,19 +2521,19 @@ ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint3
                switch (errno) {
                case EPERM:
                case EACCES:
-                       SetLastError (ERROR_ACCESS_DENIED);
+                       mono_w32error_set_last (ERROR_ACCESS_DENIED);
                        break;
                case ESRCH:
-                       SetLastError (ERROR_PROC_NOT_FOUND);
+                       mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
                        break;
                default:
-                       SetLastError (ERROR_GEN_FAILURE);
+                       mono_w32error_set_last (ERROR_GEN_FAILURE);
                }
        }
 
        return ret == 0;
 #else
-       SetLastError (ERROR_NOT_SUPPORTED);
+       mono_w32error_set_last (ERROR_NOT_SUPPORTED);
        return FALSE;
 #endif
 }
@@ -2305,13 +2545,6 @@ ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
        processtime->highDateTime = ticks >> 32;
 }
 
-static void
-wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
-{
-       processtime->lowDateTime = wapi_filetime.dwLowDateTime;
-       processtime->highDateTime = wapi_filetime.dwHighDateTime;
-}
-
 MonoBoolean
 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
 {
@@ -2334,33 +2567,33 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64
        memset (kernel_processtime, 0, sizeof (ProcessTime));
        memset (user_processtime, 0, sizeof (ProcessTime));
 
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
+       res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
+       if (!res) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
+               return FALSE;
+       }
+
+       if (!process_handle->child) {
                gint64 start_ticks, user_ticks, kernel_ticks;
 
-               mono_process_get_times (GINT_TO_POINTER (WAPI_HANDLE_TO_PID (handle)),
+               mono_process_get_times (GINT_TO_POINTER (process_handle->pid),
                        &start_ticks, &user_ticks, &kernel_ticks);
 
                ticks_to_processtime (start_ticks, creation_processtime);
-               ticks_to_processtime (user_ticks, kernel_processtime);
-               ticks_to_processtime (kernel_ticks, user_processtime);
+               ticks_to_processtime (kernel_ticks, kernel_processtime);
+               ticks_to_processtime (user_ticks, user_processtime);
                return TRUE;
        }
 
-       res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
-       if (!res) {
-               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
-               return FALSE;
-       }
-
-       wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
+       ticks_to_processtime (process_handle->create_time, creation_processtime);
 
        /* A process handle is only signalled if the process has
         * exited, otherwise exit_processtime isn't set */
        if (mono_w32handle_issignalled (handle))
-               wapifiletime_to_processtime (process_handle->exit_time, exit_processtime);
+               ticks_to_processtime (process_handle->exit_time, exit_processtime);
 
 #ifdef HAVE_GETRUSAGE
-       if (process_handle->id == getpid ()) {
+       if (process_handle->pid == getpid ()) {
                struct rusage time_data;
                if (getrusage (RUSAGE_SELF, &time_data) == 0) {
                        ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
@@ -2371,3 +2604,1751 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64
 
        return TRUE;
 }
+
+static IMAGE_SECTION_HEADER *
+get_enclosing_section_header (guint32 rva, IMAGE_NT_HEADERS32 *nt_headers)
+{
+       IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION32 (nt_headers);
+       guint32 i;
+
+       for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) {
+               guint32 size = GUINT32_FROM_LE (section->Misc.VirtualSize);
+               if (size == 0) {
+                       size = GUINT32_FROM_LE (section->SizeOfRawData);
+               }
+
+               if ((rva >= GUINT32_FROM_LE (section->VirtualAddress)) &&
+                   (rva < (GUINT32_FROM_LE (section->VirtualAddress) + size))) {
+                       return(section);
+               }
+       }
+
+       return(NULL);
+}
+
+/* This works for both 32bit and 64bit files, as the differences are
+ * all after the section header block
+ */
+static gpointer
+get_ptr_from_rva (guint32 rva, IMAGE_NT_HEADERS32 *ntheaders, gpointer file_map)
+{
+       IMAGE_SECTION_HEADER *section_header;
+       guint32 delta;
+
+       section_header = get_enclosing_section_header (rva, ntheaders);
+       if (section_header == NULL) {
+               return(NULL);
+       }
+
+       delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) -
+                         GUINT32_FROM_LE (section_header->PointerToRawData));
+
+       return((guint8 *)file_map + rva - delta);
+}
+
+static gpointer
+scan_resource_dir (IMAGE_RESOURCE_DIRECTORY *root, IMAGE_NT_HEADERS32 *nt_headers, gpointer file_map,
+       IMAGE_RESOURCE_DIRECTORY_ENTRY *entry, int level, guint32 res_id, guint32 lang_id, guint32 *size)
+{
+       IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry;
+       gboolean is_string, is_dir;
+       guint32 name_offset, dir_offset, data_offset;
+
+       swapped_entry.Name = GUINT32_FROM_LE (entry->Name);
+       swapped_entry.OffsetToData = GUINT32_FROM_LE (entry->OffsetToData);
+
+       is_string = swapped_entry.NameIsString;
+       is_dir = swapped_entry.DataIsDirectory;
+       name_offset = swapped_entry.NameOffset;
+       dir_offset = swapped_entry.OffsetToDirectory;
+       data_offset = swapped_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) {
+               IMAGE_RESOURCE_DIRECTORY *res_dir = (IMAGE_RESOURCE_DIRECTORY *)((guint8 *)root + dir_offset);
+               IMAGE_RESOURCE_DIRECTORY_ENTRY *sub_entries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(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++) {
+                       IMAGE_RESOURCE_DIRECTORY_ENTRY *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 {
+               IMAGE_RESOURCE_DATA_ENTRY *data_entry = (IMAGE_RESOURCE_DATA_ENTRY *)((guint8 *)root + data_offset);
+               *size = GUINT32_FROM_LE (data_entry->Size);
+
+               return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry->OffsetToData), nt_headers, file_map));
+       }
+}
+
+static gpointer
+find_pe_file_resources32 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, guint32 *size)
+{
+       IMAGE_DOS_HEADER *dos_header;
+       IMAGE_NT_HEADERS32 *nt_headers;
+       IMAGE_RESOURCE_DIRECTORY *resource_dir;
+       IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry;
+       guint32 resource_rva, entries, i;
+       gpointer ret = NULL;
+
+       dos_header = (IMAGE_DOS_HEADER *)file_map;
+       if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       if (map_size < sizeof(IMAGE_NT_HEADERS32) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
+
+               mono_w32error_set_last (ERROR_BAD_LENGTH);
+               return(NULL);
+       }
+
+       nt_headers = (IMAGE_NT_HEADERS32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
+       if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad NT signature 0x%x", __func__, nt_headers->Signature);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+               /* Do 64-bit stuff */
+               resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)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) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No resources in file!", __func__);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map);
+       if (resource_dir == NULL) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find resource directory", __func__);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
+       resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1);
+
+       for (i = 0; i < entries; i++) {
+               IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i];
+               ret = scan_resource_dir (resource_dir,
+                                        (IMAGE_NT_HEADERS32 *)nt_headers,
+                                        file_map, direntry, 0, res_id,
+                                        lang_id, size);
+               if (ret != NULL) {
+                       return(ret);
+               }
+       }
+
+       return(NULL);
+}
+
+static gpointer
+find_pe_file_resources64 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, guint32 *size)
+{
+       IMAGE_DOS_HEADER *dos_header;
+       IMAGE_NT_HEADERS64 *nt_headers;
+       IMAGE_RESOURCE_DIRECTORY *resource_dir;
+       IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry;
+       guint32 resource_rva, entries, i;
+       gpointer ret = NULL;
+
+       dos_header = (IMAGE_DOS_HEADER *)file_map;
+       if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       if (map_size < sizeof(IMAGE_NT_HEADERS64) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
+
+               mono_w32error_set_last (ERROR_BAD_LENGTH);
+               return(NULL);
+       }
+
+       nt_headers = (IMAGE_NT_HEADERS64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
+       if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Bad NT signature 0x%x", __func__,
+                          nt_headers->Signature);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+               /* Do 64-bit stuff */
+               resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)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) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: No resources in file!", __func__);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map);
+       if (resource_dir == NULL) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find resource directory", __func__);
+
+               mono_w32error_set_last (ERROR_INVALID_DATA);
+               return(NULL);
+       }
+
+       entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
+       resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1);
+
+       for (i = 0; i < entries; i++) {
+               IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i];
+               ret = scan_resource_dir (resource_dir,
+                                        (IMAGE_NT_HEADERS32 *)nt_headers,
+                                        file_map, direntry, 0, res_id,
+                                        lang_id, size);
+               if (ret != NULL) {
+                       return(ret);
+               }
+       }
+
+       return(NULL);
+}
+
+static gpointer
+find_pe_file_resources (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, guint32 *size)
+{
+       /* Figure this out when we support 64bit PE files */
+       if (1) {
+               return find_pe_file_resources32 (file_map, map_size, res_id,
+                                                lang_id, size);
+       } else {
+               return find_pe_file_resources64 (file_map, map_size, res_id,
+                                                lang_id, size);
+       }
+}
+
+static gpointer
+map_pe_file (gunichar2 *filename, gint32 *map_size, void **handle)
+{
+       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) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
+
+               mono_w32error_set_last (ERROR_INVALID_NAME);
+               return(NULL);
+       }
+
+       fd = open (filename_ext, O_RDONLY, 0);
+       if (fd == -1 && (errno == ENOENT || errno == ENOTDIR) && IS_PORTABILITY_SET) {
+               gint saved_errno;
+               gchar *located_filename;
+
+               saved_errno = errno;
+
+               located_filename = mono_portability_find_file (filename_ext, TRUE);
+               if (!located_filename) {
+                       errno = saved_errno;
+
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s (1): %s", __func__, filename_ext, strerror (errno));
+
+                       g_free (filename_ext);
+
+                       mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
+                       return NULL;
+               }
+
+               fd = open (located_filename, O_RDONLY, 0);
+               if (fd == -1) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error opening file %s (2): %s", __func__, filename_ext, strerror (errno));
+
+                       g_free (filename_ext);
+                       g_free (located_filename);
+
+                       mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
+                       return NULL;
+               }
+
+               g_free (located_filename);
+       }
+
+       if (fstat (fd, &statbuf) == -1) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error stat()ing file %s: %s", __func__, filename_ext, strerror (errno));
+
+               mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+       *map_size = statbuf.st_size;
+
+       /* Check basic file size */
+       if (statbuf.st_size < sizeof(IMAGE_DOS_HEADER)) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File %s is too small: %lld", __func__, filename_ext, (long long) statbuf.st_size);
+
+               mono_w32error_set_last (ERROR_BAD_LENGTH);
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+
+       file_map = mono_file_map (statbuf.st_size, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, 0, handle);
+       if (file_map == NULL) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Error mmap()int file %s: %s", __func__, filename_ext, strerror (errno));
+
+               mono_w32error_set_last (mono_w32error_unix_to_win32 (errno));
+               g_free (filename_ext);
+               close (fd);
+               return(NULL);
+       }
+
+       /* Don't need the fd any more */
+       close (fd);
+       g_free (filename_ext);
+
+       return(file_map);
+}
+
+static void
+unmap_pe_file (gpointer file_map, void *handle)
+{
+       mono_file_unmap (file_map, handle);
+}
+
+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 (*str1 != *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 */
+       ALIGN32 (data);
+
+       return(data);
+}
+
+static gconstpointer
+get_fixedfileinfo_block (gconstpointer data, version_data *block)
+{
+       gconstpointer data_ptr;
+       VS_FIXEDFILEINFO *ffi;
+
+       data_ptr = get_versioninfo_block (data, block);
+
+       if (block->value_len != sizeof(VS_FIXEDFILEINFO)) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: FIXEDFILEINFO size mismatch", __func__);
+               return(NULL);
+       }
+
+       if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: VS_VERSION_INFO mismatch", __func__);
+
+               return(NULL);
+       }
+
+       ffi = ((VS_FIXEDFILEINFO *)data_ptr);
+       if ((ffi->dwSignature != VS_FFI_SIGNATURE) ||
+           (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) {
+               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: FIXEDFILEINFO bad signature", __func__);
+
+               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 */
+       char *orig_data_ptr = (char *)data_ptr - 28;
+
+       /* data_ptr is pointing at an array of one or more String blocks
+        * with total length (not including alignment padding) of
+        * data_len
+        */
+       while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
+               /* align on a 32-bit boundary */
+               ALIGN32 (data_ptr);
+
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+
+                       return(NULL);
+               }
+
+               string_len = string_len + block->data_len;
+
+               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
+ *
+ * If lang == NULL it means we're just stepping through this block
+ */
+static gconstpointer
+get_stringtable_block (gconstpointer data_ptr, gchar *lang, 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 */
+       gchar *found_lang;
+       gchar *lowercase_lang;
+
+       /* 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 */
+               ALIGN32 (data_ptr);
+
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+                       return(NULL);
+               }
+
+               string_len = string_len + block->data_len;
+
+               found_lang = g_utf16_to_utf8 (block->key, 8, NULL, NULL, NULL);
+               if (found_lang == NULL) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid language key, giving up", __func__);
+                       return(NULL);
+               }
+
+               lowercase_lang = g_utf8_strdown (found_lang, -1);
+               g_free (found_lang);
+               found_lang = lowercase_lang;
+               lowercase_lang = NULL;
+
+               if (lang != NULL && !strcmp (found_lang, lang)) {
+                       /* 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);
+               }
+
+               g_free (found_lang);
+
+               if (data_ptr == NULL) {
+                       /* Child block hit padding */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
+                       return(NULL);
+               }
+       }
+
+       return(data_ptr);
+}
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+static gconstpointer
+big_up_string_block (gconstpointer data_ptr, version_data *block)
+{
+       guint16 data_len = block->data_len;
+       guint16 string_len = 28; /* Length of the StringTable block */
+       gchar *big_value;
+       char *orig_data_ptr = (char *)data_ptr - 28;
+
+       /* data_ptr is pointing at an array of one or more String
+        * blocks with total length (not including alignment padding)
+        * of data_len
+        */
+       while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
+               /* align on a 32-bit boundary */
+               ALIGN32 (data_ptr);
+
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+                       return(NULL);
+               }
+
+               string_len = string_len + block->data_len;
+
+               big_value = g_convert ((gchar *)block->key,
+                                      unicode_chars (block->key) * 2,
+                                      "UTF-16BE", "UTF-16LE", NULL, NULL,
+                                      NULL);
+               if (big_value == NULL) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid string, giving up", __func__);
+                       return(NULL);
+               }
+
+               /* The swapped string should be exactly the same
+                * length as the original little-endian one, but only
+                * copy the number of original chars just to be on the
+                * safe side
+                */
+               memcpy (block->key, big_value, unicode_chars (block->key) * 2);
+               g_free (big_value);
+
+               big_value = g_convert ((gchar *)data_ptr,
+                                      unicode_chars (data_ptr) * 2,
+                                      "UTF-16BE", "UTF-16LE", NULL, NULL,
+                                      NULL);
+               if (big_value == NULL) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid data string, giving up", __func__);
+                       return(NULL);
+               }
+               memcpy ((gpointer)data_ptr, big_value,
+                       unicode_chars (data_ptr) * 2);
+               g_free (big_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
+big_up_stringtable_block (gconstpointer data_ptr, version_data *block)
+{
+       guint16 data_len = block->data_len;
+       guint16 string_len = 36; /* length of the StringFileInfo block */
+       gchar *big_value;
+
+       /* 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 */
+               ALIGN32 (data_ptr);
+
+               data_ptr = get_versioninfo_block (data_ptr, block);
+               if (block->data_len == 0) {
+                       /* We must have hit padding, so give up
+                        * processing now
+                        */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+                       return(NULL);
+               }
+
+               string_len = string_len + block->data_len;
+
+               big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE",
+                                      "UTF-16LE", NULL, NULL, NULL);
+               if (big_value == NULL) {
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't find a valid string, giving up", __func__);
+                       return(NULL);
+               }
+
+               memcpy (block->key, big_value, 16);
+               g_free (big_value);
+
+               data_ptr = big_up_string_block (data_ptr, block);
+
+               if (data_ptr == NULL) {
+                       /* Child block hit padding */
+                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
+                       return(NULL);
+               }
+       }
+
+       return(data_ptr);
+}
+
+/* Follows the data structures and turns all UTF-16 strings from the
+ * LE found in the resource section into UTF-16BE
+ */
+static void
+big_up (gconstpointer datablock, guint32 size)
+{
+       gconstpointer data_ptr;
+       gint32 data_len; /* signed to guard against underflow */
+       version_data block;
+
+       data_ptr = get_fixedfileinfo_block (datablock, &block);
+       if (data_ptr != NULL) {
+               VS_FIXEDFILEINFO *ffi = (VS_FIXEDFILEINFO *)data_ptr;
+
+               /* Byteswap all the fields */
+               ffi->dwFileVersionMS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionMS);
+               ffi->dwFileVersionLS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionLS);
+               ffi->dwProductVersionMS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionMS);
+               ffi->dwProductVersionLS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionLS);
+               ffi->dwFileFlagsMask = GUINT32_SWAP_LE_BE (ffi->dwFileFlagsMask);
+               ffi->dwFileFlags = GUINT32_SWAP_LE_BE (ffi->dwFileFlags);
+               ffi->dwFileOS = GUINT32_SWAP_LE_BE (ffi->dwFileOS);
+               ffi->dwFileType = GUINT32_SWAP_LE_BE (ffi->dwFileType);
+               ffi->dwFileSubtype = GUINT32_SWAP_LE_BE (ffi->dwFileSubtype);
+               ffi->dwFileDateMS = GUINT32_SWAP_LE_BE (ffi->dwFileDateMS);
+               ffi->dwFileDateLS = GUINT32_SWAP_LE_BE (ffi->dwFileDateLS);
+
+               /* The FFI and header occupies the first 92 bytes
+                */
+               data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
+               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 */
+                       ALIGN32 (data_ptr);
+
+                       data_ptr = get_versioninfo_block (data_ptr, &block);
+                       if (block.data_len == 0) {
+                               /* We must have hit padding, so give
+                                * up processing now
+                                */
+                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+                               return;
+                       }
+
+                       data_len = data_len - block.data_len;
+
+                       if (unicode_string_equals (block.key, "VarFileInfo")) {
+                               data_ptr = get_varfileinfo_block (data_ptr,
+                                                                 &block);
+                               data_ptr = ((guchar *)data_ptr) + block.value_len;
+                       } else if (unicode_string_equals (block.key,
+                                                         "StringFileInfo")) {
+                               data_ptr = big_up_stringtable_block (data_ptr,
+                                                                    &block);
+                       } else {
+                               /* Bogus data */
+                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Not a valid VERSIONINFO child block", __func__);
+                               return;
+                       }
+
+                       if (data_ptr == NULL) {
+                               /* Child block hit padding */
+                               mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
+                               return;
+                       }
+               }
+       }
+}
+#endif
+
+guint32
+mono_w32process_get_fileversion_info_size (gunichar2 *filename, guint32 *handle)
+{
+       gpointer file_map;
+       gpointer versioninfo;
+       void *map_handle;
+       gint32 map_size;
+       guint32 size;
+
+       /* This value is unused, but set to zero */
+       *handle = 0;
+
+       file_map = map_pe_file (filename, &map_size, &map_handle);
+       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_handle);
+
+       return(size);
+}
+
+gboolean
+mono_w32process_get_fileversion_info (gunichar2 *filename, guint32 handle G_GNUC_UNUSED, guint32 len, gpointer data)
+{
+       gpointer file_map;
+       gpointer versioninfo;
+       void *map_handle;
+       gint32 map_size;
+       guint32 size;
+       gboolean ret = FALSE;
+
+       file_map = map_pe_file (filename, &map_size, &map_handle);
+       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
+                * mono_w32process_ver_query_value() 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;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+               big_up (data, size);
+#endif
+       }
+
+       unmap_pe_file (file_map, map_handle);
+
+       return(ret);
+}
+
+gboolean
+mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len)
+{
+       gchar *subblock_utf8, *lang_utf8 = NULL;
+       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;
+       gchar *lowercase_lang;
+
+       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));
+               lang_utf8 = g_utf16_to_utf8 (lang, 8, NULL, NULL, NULL);
+               lowercase_lang = g_utf8_strdown (lang_utf8, -1);
+               g_free (lang_utf8);
+               lang_utf8 = lowercase_lang;
+               lowercase_lang = NULL;
+               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(VS_FIXEDFILEINFO);
+                       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 */
+                               ALIGN32 (data_ptr);
+
+                               data_ptr = get_versioninfo_block (data_ptr,
+                                                                 &block);
+                               if (block.data_len == 0) {
+                                       /* We must have hit padding,
+                                        * so give up processing now
+                                        */
+                                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Hit 0-length block, giving up", __func__);
+                                       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_utf8, string_key, &string_value, &string_value_len, &block);
+                                       if (want_string &&
+                                           string_value != NULL &&
+                                           string_value_len != 0) {
+                                               *buffer = string_value;
+                                               *len = unicode_chars ((const gunichar2 *)string_value) + 1; /* Include trailing null */
+                                               ret = TRUE;
+                                               goto done;
+                                       }
+                               } else {
+                                       /* Bogus data */
+                                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Not a valid VERSIONINFO child block", __func__);
+                                       goto done;
+                               }
+
+                               if (data_ptr == NULL) {
+                                       /* Child block hit padding */
+                                       mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Child block hit 0-length block, giving up", __func__);
+                                       goto done;
+                               }
+                       }
+               }
+       }
+
+  done:
+       if (lang_utf8) {
+               g_free (lang_utf8);
+       }
+
+       g_free (subblock_utf8);
+       return(ret);
+}
+
+static guint32
+copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text)
+{
+       gunichar2 *unitext;
+       int chars = strlen (text);
+       int ret;
+
+       unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL);
+       g_assert (unitext != NULL);
+
+       if (chars < (lang_len - 1)) {
+               memcpy (lang_out, (gpointer)unitext, chars * 2);
+               lang_out[chars] = '\0';
+               ret = chars;
+       } else {
+               memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2);
+               lang_out[lang_len] = '\0';
+               ret = lang_len;
+       }
+
+       g_free (unitext);
+
+       return(ret);
+}
+
+guint32
+mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
+{
+       int primary, secondary;
+       const char *name = NULL;
+
+       primary = lang & 0x3FF;
+       secondary = (lang >> 10) & 0x3F;
+
+       switch(primary) {
+       case 0x00:
+               switch (secondary) {
+               case 0x01: name = "Process Default Language"; break;
+               }
+               break;
+       case 0x01:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Arabic (Saudi Arabia)"; break;
+               case 0x02: name = "Arabic (Iraq)"; break;
+               case 0x03: name = "Arabic (Egypt)"; break;
+               case 0x04: name = "Arabic (Libya)"; break;
+               case 0x05: name = "Arabic (Algeria)"; break;
+               case 0x06: name = "Arabic (Morocco)"; break;
+               case 0x07: name = "Arabic (Tunisia)"; break;
+               case 0x08: name = "Arabic (Oman)"; break;
+               case 0x09: name = "Arabic (Yemen)"; break;
+               case 0x0a: name = "Arabic (Syria)"; break;
+               case 0x0b: name = "Arabic (Jordan)"; break;
+               case 0x0c: name = "Arabic (Lebanon)"; break;
+               case 0x0d: name = "Arabic (Kuwait)"; break;
+               case 0x0e: name = "Arabic (U.A.E.)"; break;
+               case 0x0f: name = "Arabic (Bahrain)"; break;
+               case 0x10: name = "Arabic (Qatar)"; break;
+               }
+               break;
+       case 0x02:
+               switch (secondary) {
+               case 0x00: name = "Bulgarian (Bulgaria)"; break;
+               case 0x01: name = "Bulgarian"; break;
+               }
+               break;
+       case 0x03:
+               switch (secondary) {
+               case 0x00: name = "Catalan (Spain)"; break;
+               case 0x01: name = "Catalan"; break;
+               }
+               break;
+       case 0x04:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Chinese (Taiwan)"; break;
+               case 0x02: name = "Chinese (PRC)"; break;
+               case 0x03: name = "Chinese (Hong Kong S.A.R.)"; break;
+               case 0x04: name = "Chinese (Singapore)"; break;
+               case 0x05: name = "Chinese (Macau S.A.R.)"; break;
+               }
+               break;
+       case 0x05:
+               switch (secondary) {
+               case 0x00: name = "Czech (Czech Republic)"; break;
+               case 0x01: name = "Czech"; break;
+               }
+               break;
+       case 0x06:
+               switch (secondary) {
+               case 0x00: name = "Danish (Denmark)"; break;
+               case 0x01: name = "Danish"; break;
+               }
+               break;
+       case 0x07:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "German (Germany)"; break;
+               case 0x02: name = "German (Switzerland)"; break;
+               case 0x03: name = "German (Austria)"; break;
+               case 0x04: name = "German (Luxembourg)"; break;
+               case 0x05: name = "German (Liechtenstein)"; break;
+               }
+               break;
+       case 0x08:
+               switch (secondary) {
+               case 0x00: name = "Greek (Greece)"; break;
+               case 0x01: name = "Greek"; break;
+               }
+               break;
+       case 0x09:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "English (United States)"; break;
+               case 0x02: name = "English (United Kingdom)"; break;
+               case 0x03: name = "English (Australia)"; break;
+               case 0x04: name = "English (Canada)"; break;
+               case 0x05: name = "English (New Zealand)"; break;
+               case 0x06: name = "English (Ireland)"; break;
+               case 0x07: name = "English (South Africa)"; break;
+               case 0x08: name = "English (Jamaica)"; break;
+               case 0x09: name = "English (Caribbean)"; break;
+               case 0x0a: name = "English (Belize)"; break;
+               case 0x0b: name = "English (Trinidad and Tobago)"; break;
+               case 0x0c: name = "English (Zimbabwe)"; break;
+               case 0x0d: name = "English (Philippines)"; break;
+               case 0x10: name = "English (India)"; break;
+               case 0x11: name = "English (Malaysia)"; break;
+               case 0x12: name = "English (Singapore)"; break;
+               }
+               break;
+       case 0x0a:
+               switch (secondary) {
+               case 0x00: name = "Spanish (Spain)"; break;
+               case 0x01: name = "Spanish (Traditional Sort)"; break;
+               case 0x02: name = "Spanish (Mexico)"; break;
+               case 0x03: name = "Spanish (International Sort)"; break;
+               case 0x04: name = "Spanish (Guatemala)"; break;
+               case 0x05: name = "Spanish (Costa Rica)"; break;
+               case 0x06: name = "Spanish (Panama)"; break;
+               case 0x07: name = "Spanish (Dominican Republic)"; break;
+               case 0x08: name = "Spanish (Venezuela)"; break;
+               case 0x09: name = "Spanish (Colombia)"; break;
+               case 0x0a: name = "Spanish (Peru)"; break;
+               case 0x0b: name = "Spanish (Argentina)"; break;
+               case 0x0c: name = "Spanish (Ecuador)"; break;
+               case 0x0d: name = "Spanish (Chile)"; break;
+               case 0x0e: name = "Spanish (Uruguay)"; break;
+               case 0x0f: name = "Spanish (Paraguay)"; break;
+               case 0x10: name = "Spanish (Bolivia)"; break;
+               case 0x11: name = "Spanish (El Salvador)"; break;
+               case 0x12: name = "Spanish (Honduras)"; break;
+               case 0x13: name = "Spanish (Nicaragua)"; break;
+               case 0x14: name = "Spanish (Puerto Rico)"; break;
+               case 0x15: name = "Spanish (United States)"; break;
+               }
+               break;
+       case 0x0b:
+               switch (secondary) {
+               case 0x00: name = "Finnish (Finland)"; break;
+               case 0x01: name = "Finnish"; break;
+               }
+               break;
+       case 0x0c:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "French (France)"; break;
+               case 0x02: name = "French (Belgium)"; break;
+               case 0x03: name = "French (Canada)"; break;
+               case 0x04: name = "French (Switzerland)"; break;
+               case 0x05: name = "French (Luxembourg)"; break;
+               case 0x06: name = "French (Monaco)"; break;
+               }
+               break;
+       case 0x0d:
+               switch (secondary) {
+               case 0x00: name = "Hebrew (Israel)"; break;
+               case 0x01: name = "Hebrew"; break;
+               }
+               break;
+       case 0x0e:
+               switch (secondary) {
+               case 0x00: name = "Hungarian (Hungary)"; break;
+               case 0x01: name = "Hungarian"; break;
+               }
+               break;
+       case 0x0f:
+               switch (secondary) {
+               case 0x00: name = "Icelandic (Iceland)"; break;
+               case 0x01: name = "Icelandic"; break;
+               }
+               break;
+       case 0x10:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Italian (Italy)"; break;
+               case 0x02: name = "Italian (Switzerland)"; break;
+               }
+               break;
+       case 0x11:
+               switch (secondary) {
+               case 0x00: name = "Japanese (Japan)"; break;
+               case 0x01: name = "Japanese"; break;
+               }
+               break;
+       case 0x12:
+               switch (secondary) {
+               case 0x00: name = "Korean (Korea)"; break;
+               case 0x01: name = "Korean"; break;
+               }
+               break;
+       case 0x13:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Dutch (Netherlands)"; break;
+               case 0x02: name = "Dutch (Belgium)"; break;
+               }
+               break;
+       case 0x14:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Norwegian (Bokmal)"; break;
+               case 0x02: name = "Norwegian (Nynorsk)"; break;
+               }
+               break;
+       case 0x15:
+               switch (secondary) {
+               case 0x00: name = "Polish (Poland)"; break;
+               case 0x01: name = "Polish"; break;
+               }
+               break;
+       case 0x16:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Portuguese (Brazil)"; break;
+               case 0x02: name = "Portuguese (Portugal)"; break;
+               }
+               break;
+       case 0x17:
+               switch (secondary) {
+               case 0x01: name = "Romansh (Switzerland)"; break;
+               }
+               break;
+       case 0x18:
+               switch (secondary) {
+               case 0x00: name = "Romanian (Romania)"; break;
+               case 0x01: name = "Romanian"; break;
+               }
+               break;
+       case 0x19:
+               switch (secondary) {
+               case 0x00: name = "Russian (Russia)"; break;
+               case 0x01: name = "Russian"; break;
+               }
+               break;
+       case 0x1a:
+               switch (secondary) {
+               case 0x00: name = "Croatian (Croatia)"; break;
+               case 0x01: name = "Croatian"; break;
+               case 0x02: name = "Serbian (Latin)"; break;
+               case 0x03: name = "Serbian (Cyrillic)"; break;
+               case 0x04: name = "Croatian (Bosnia and Herzegovina)"; break;
+               case 0x05: name = "Bosnian (Latin, Bosnia and Herzegovina)"; break;
+               case 0x06: name = "Serbian (Latin, Bosnia and Herzegovina)"; break;
+               case 0x07: name = "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
+               case 0x08: name = "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
+               }
+               break;
+       case 0x1b:
+               switch (secondary) {
+               case 0x00: name = "Slovak (Slovakia)"; break;
+               case 0x01: name = "Slovak"; break;
+               }
+               break;
+       case 0x1c:
+               switch (secondary) {
+               case 0x00: name = "Albanian (Albania)"; break;
+               case 0x01: name = "Albanian"; break;
+               }
+               break;
+       case 0x1d:
+               switch (secondary) {
+               case 0x00: name = "Swedish (Sweden)"; break;
+               case 0x01: name = "Swedish"; break;
+               case 0x02: name = "Swedish (Finland)"; break;
+               }
+               break;
+       case 0x1e:
+               switch (secondary) {
+               case 0x00: name = "Thai (Thailand)"; break;
+               case 0x01: name = "Thai"; break;
+               }
+               break;
+       case 0x1f:
+               switch (secondary) {
+               case 0x00: name = "Turkish (Turkey)"; break;
+               case 0x01: name = "Turkish"; break;
+               }
+               break;
+       case 0x20:
+               switch (secondary) {
+               case 0x00: name = "Urdu (Islamic Republic of Pakistan)"; break;
+               case 0x01: name = "Urdu"; break;
+               }
+               break;
+       case 0x21:
+               switch (secondary) {
+               case 0x00: name = "Indonesian (Indonesia)"; break;
+               case 0x01: name = "Indonesian"; break;
+               }
+               break;
+       case 0x22:
+               switch (secondary) {
+               case 0x00: name = "Ukrainian (Ukraine)"; break;
+               case 0x01: name = "Ukrainian"; break;
+               }
+               break;
+       case 0x23:
+               switch (secondary) {
+               case 0x00: name = "Belarusian (Belarus)"; break;
+               case 0x01: name = "Belarusian"; break;
+               }
+               break;
+       case 0x24:
+               switch (secondary) {
+               case 0x00: name = "Slovenian (Slovenia)"; break;
+               case 0x01: name = "Slovenian"; break;
+               }
+               break;
+       case 0x25:
+               switch (secondary) {
+               case 0x00: name = "Estonian (Estonia)"; break;
+               case 0x01: name = "Estonian"; break;
+               }
+               break;
+       case 0x26:
+               switch (secondary) {
+               case 0x00: name = "Latvian (Latvia)"; break;
+               case 0x01: name = "Latvian"; break;
+               }
+               break;
+       case 0x27:
+               switch (secondary) {
+               case 0x00: name = "Lithuanian (Lithuania)"; break;
+               case 0x01: name = "Lithuanian"; break;
+               }
+               break;
+       case 0x28:
+               switch (secondary) {
+               case 0x01: name = "Tajik (Tajikistan)"; break;
+               }
+               break;
+       case 0x29:
+               switch (secondary) {
+               case 0x00: name = "Farsi (Iran)"; break;
+               case 0x01: name = "Farsi"; break;
+               }
+               break;
+       case 0x2a:
+               switch (secondary) {
+               case 0x00: name = "Vietnamese (Viet Nam)"; break;
+               case 0x01: name = "Vietnamese"; break;
+               }
+               break;
+       case 0x2b:
+               switch (secondary) {
+               case 0x00: name = "Armenian (Armenia)"; break;
+               case 0x01: name = "Armenian"; break;
+               }
+               break;
+       case 0x2c:
+               switch (secondary) {
+               case 0x00: name = "Azeri (Latin) (Azerbaijan)"; break;
+               case 0x01: name = "Azeri (Latin)"; break;
+               case 0x02: name = "Azeri (Cyrillic)"; break;
+               }
+               break;
+       case 0x2d:
+               switch (secondary) {
+               case 0x00: name = "Basque (Spain)"; break;
+               case 0x01: name = "Basque"; break;
+               }
+               break;
+       case 0x2e:
+               switch (secondary) {
+               case 0x01: name = "Upper Sorbian (Germany)"; break;
+               case 0x02: name = "Lower Sorbian (Germany)"; break;
+               }
+               break;
+       case 0x2f:
+               switch (secondary) {
+               case 0x00: name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
+               case 0x01: name = "FYRO Macedonian"; break;
+               }
+               break;
+       case 0x32:
+               switch (secondary) {
+               case 0x00: name = "Tswana (South Africa)"; break;
+               case 0x01: name = "Tswana"; break;
+               }
+               break;
+       case 0x34:
+               switch (secondary) {
+               case 0x00: name = "Xhosa (South Africa)"; break;
+               case 0x01: name = "Xhosa"; break;
+               }
+               break;
+       case 0x35:
+               switch (secondary) {
+               case 0x00: name = "Zulu (South Africa)"; break;
+               case 0x01: name = "Zulu"; break;
+               }
+               break;
+       case 0x36:
+               switch (secondary) {
+               case 0x00: name = "Afrikaans (South Africa)"; break;
+               case 0x01: name = "Afrikaans"; break;
+               }
+               break;
+       case 0x37:
+               switch (secondary) {
+               case 0x00: name = "Georgian (Georgia)"; break;
+               case 0x01: name = "Georgian"; break;
+               }
+               break;
+       case 0x38:
+               switch (secondary) {
+               case 0x00: name = "Faroese (Faroe Islands)"; break;
+               case 0x01: name = "Faroese"; break;
+               }
+               break;
+       case 0x39:
+               switch (secondary) {
+               case 0x00: name = "Hindi (India)"; break;
+               case 0x01: name = "Hindi"; break;
+               }
+               break;
+       case 0x3a:
+               switch (secondary) {
+               case 0x00: name = "Maltese (Malta)"; break;
+               case 0x01: name = "Maltese"; break;
+               }
+               break;
+       case 0x3b:
+               switch (secondary) {
+               case 0x00: name = "Sami (Northern) (Norway)"; break;
+               case 0x01: name = "Sami, Northern (Norway)"; break;
+               case 0x02: name = "Sami, Northern (Sweden)"; break;
+               case 0x03: name = "Sami, Northern (Finland)"; break;
+               case 0x04: name = "Sami, Lule (Norway)"; break;
+               case 0x05: name = "Sami, Lule (Sweden)"; break;
+               case 0x06: name = "Sami, Southern (Norway)"; break;
+               case 0x07: name = "Sami, Southern (Sweden)"; break;
+               case 0x08: name = "Sami, Skolt (Finland)"; break;
+               case 0x09: name = "Sami, Inari (Finland)"; break;
+               }
+               break;
+       case 0x3c:
+               switch (secondary) {
+               case 0x02: name = "Irish (Ireland)"; break;
+               }
+               break;
+       case 0x3e:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Malay (Malaysia)"; break;
+               case 0x02: name = "Malay (Brunei Darussalam)"; break;
+               }
+               break;
+       case 0x3f:
+               switch (secondary) {
+               case 0x00: name = "Kazakh (Kazakhstan)"; break;
+               case 0x01: name = "Kazakh"; break;
+               }
+               break;
+       case 0x40:
+               switch (secondary) {
+               case 0x00: name = "Kyrgyz (Kyrgyzstan)"; break;
+               case 0x01: name = "Kyrgyz (Cyrillic)"; break;
+               }
+               break;
+       case 0x41:
+               switch (secondary) {
+               case 0x00: name = "Swahili (Kenya)"; break;
+               case 0x01: name = "Swahili"; break;
+               }
+               break;
+       case 0x42:
+               switch (secondary) {
+               case 0x01: name = "Turkmen (Turkmenistan)"; break;
+               }
+               break;
+       case 0x43:
+               switch (secondary) {
+               case 0x00: name = "Uzbek (Latin) (Uzbekistan)"; break;
+               case 0x01: name = "Uzbek (Latin)"; break;
+               case 0x02: name = "Uzbek (Cyrillic)"; break;
+               }
+               break;
+       case 0x44:
+               switch (secondary) {
+               case 0x00: name = "Tatar (Russia)"; break;
+               case 0x01: name = "Tatar"; break;
+               }
+               break;
+       case 0x45:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Bengali (India)"; break;
+               }
+               break;
+       case 0x46:
+               switch (secondary) {
+               case 0x00: name = "Punjabi (India)"; break;
+               case 0x01: name = "Punjabi"; break;
+               }
+               break;
+       case 0x47:
+               switch (secondary) {
+               case 0x00: name = "Gujarati (India)"; break;
+               case 0x01: name = "Gujarati"; break;
+               }
+               break;
+       case 0x49:
+               switch (secondary) {
+               case 0x00: name = "Tamil (India)"; break;
+               case 0x01: name = "Tamil"; break;
+               }
+               break;
+       case 0x4a:
+               switch (secondary) {
+               case 0x00: name = "Telugu (India)"; break;
+               case 0x01: name = "Telugu"; break;
+               }
+               break;
+       case 0x4b:
+               switch (secondary) {
+               case 0x00: name = "Kannada (India)"; break;
+               case 0x01: name = "Kannada"; break;
+               }
+               break;
+       case 0x4c:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Malayalam (India)"; break;
+               }
+               break;
+       case 0x4d:
+               switch (secondary) {
+               case 0x01: name = "Assamese (India)"; break;
+               }
+               break;
+       case 0x4e:
+               switch (secondary) {
+               case 0x00: name = "Marathi (India)"; break;
+               case 0x01: name = "Marathi"; break;
+               }
+               break;
+       case 0x4f:
+               switch (secondary) {
+               case 0x00: name = "Sanskrit (India)"; break;
+               case 0x01: name = "Sanskrit"; break;
+               }
+               break;
+       case 0x50:
+               switch (secondary) {
+               case 0x00: name = "Mongolian (Mongolia)"; break;
+               case 0x01: name = "Mongolian (Cyrillic)"; break;
+               case 0x02: name = "Mongolian (PRC)"; break;
+               }
+               break;
+       case 0x51:
+               switch (secondary) {
+               case 0x01: name = "Tibetan (PRC)"; break;
+               case 0x02: name = "Tibetan (Bhutan)"; break;
+               }
+               break;
+       case 0x52:
+               switch (secondary) {
+               case 0x00: name = "Welsh (United Kingdom)"; break;
+               case 0x01: name = "Welsh"; break;
+               }
+               break;
+       case 0x53:
+               switch (secondary) {
+               case 0x01: name = "Khmer (Cambodia)"; break;
+               }
+               break;
+       case 0x54:
+               switch (secondary) {
+               case 0x01: name = "Lao (Lao PDR)"; break;
+               }
+               break;
+       case 0x56:
+               switch (secondary) {
+               case 0x00: name = "Galician (Spain)"; break;
+               case 0x01: name = "Galician"; break;
+               }
+               break;
+       case 0x57:
+               switch (secondary) {
+               case 0x00: name = "Konkani (India)"; break;
+               case 0x01: name = "Konkani"; break;
+               }
+               break;
+       case 0x5a:
+               switch (secondary) {
+               case 0x00: name = "Syriac (Syria)"; break;
+               case 0x01: name = "Syriac"; break;
+               }
+               break;
+       case 0x5b:
+               switch (secondary) {
+               case 0x01: name = "Sinhala (Sri Lanka)"; break;
+               }
+               break;
+       case 0x5d:
+               switch (secondary) {
+               case 0x01: name = "Inuktitut (Syllabics, Canada)"; break;
+               case 0x02: name = "Inuktitut (Latin, Canada)"; break;
+               }
+               break;
+       case 0x5e:
+               switch (secondary) {
+               case 0x01: name = "Amharic (Ethiopia)"; break;
+               }
+               break;
+       case 0x5f:
+               switch (secondary) {
+               case 0x02: name = "Tamazight (Algeria, Latin)"; break;
+               }
+               break;
+       case 0x61:
+               switch (secondary) {
+               case 0x01: name = "Nepali (Nepal)"; break;
+               }
+               break;
+       case 0x62:
+               switch (secondary) {
+               case 0x01: name = "Frisian (Netherlands)"; break;
+               }
+               break;
+       case 0x63:
+               switch (secondary) {
+               case 0x01: name = "Pashto (Afghanistan)"; break;
+               }
+               break;
+       case 0x64:
+               switch (secondary) {
+               case 0x01: name = "Filipino (Philippines)"; break;
+               }
+               break;
+       case 0x65:
+               switch (secondary) {
+               case 0x00: name = "Divehi (Maldives)"; break;
+               case 0x01: name = "Divehi"; break;
+               }
+               break;
+       case 0x68:
+               switch (secondary) {
+               case 0x01: name = "Hausa (Nigeria, Latin)"; break;
+               }
+               break;
+       case 0x6a:
+               switch (secondary) {
+               case 0x01: name = "Yoruba (Nigeria)"; break;
+               }
+               break;
+       case 0x6b:
+               switch (secondary) {
+               case 0x00:
+               case 0x01: name = "Quechua (Bolivia)"; break;
+               case 0x02: name = "Quechua (Ecuador)"; break;
+               case 0x03: name = "Quechua (Peru)"; break;
+               }
+               break;
+       case 0x6c:
+               switch (secondary) {
+               case 0x00: name = "Northern Sotho (South Africa)"; break;
+               case 0x01: name = "Northern Sotho"; break;
+               }
+               break;
+       case 0x6d:
+               switch (secondary) {
+               case 0x01: name = "Bashkir (Russia)"; break;
+               }
+               break;
+       case 0x6e:
+               switch (secondary) {
+               case 0x01: name = "Luxembourgish (Luxembourg)"; break;
+               }
+               break;
+       case 0x6f:
+               switch (secondary) {
+               case 0x01: name = "Greenlandic (Greenland)"; break;
+               }
+               break;
+       case 0x78:
+               switch (secondary) {
+               case 0x01: name = "Yi (PRC)"; break;
+               }
+               break;
+       case 0x7a:
+               switch (secondary) {
+               case 0x01: name = "Mapudungun (Chile)"; break;
+               }
+               break;
+       case 0x7c:
+               switch (secondary) {
+               case 0x01: name = "Mohawk (Mohawk)"; break;
+               }
+               break;
+       case 0x7e:
+               switch (secondary) {
+               case 0x01: name = "Breton (France)"; break;
+               }
+               break;
+       case 0x7f:
+               switch (secondary) {
+               case 0x00: name = "Invariant Language (Invariant Country)"; break;
+               }
+               break;
+       case 0x80:
+               switch (secondary) {
+               case 0x01: name = "Uighur (PRC)"; break;
+               }
+               break;
+       case 0x81:
+               switch (secondary) {
+               case 0x00: name = "Maori (New Zealand)"; break;
+               case 0x01: name = "Maori"; break;
+               }
+               break;
+       case 0x83:
+               switch (secondary) {
+               case 0x01: name = "Corsican (France)"; break;
+               }
+               break;
+       case 0x84:
+               switch (secondary) {
+               case 0x01: name = "Alsatian (France)"; break;
+               }
+               break;
+       case 0x85:
+               switch (secondary) {
+               case 0x01: name = "Yakut (Russia)"; break;
+               }
+               break;
+       case 0x86:
+               switch (secondary) {
+               case 0x01: name = "K'iche (Guatemala)"; break;
+               }
+               break;
+       case 0x87:
+               switch (secondary) {
+               case 0x01: name = "Kinyarwanda (Rwanda)"; break;
+               }
+               break;
+       case 0x88:
+               switch (secondary) {
+               case 0x01: name = "Wolof (Senegal)"; break;
+               }
+               break;
+       case 0x8c:
+               switch (secondary) {
+               case 0x01: name = "Dari (Afghanistan)"; break;
+               }
+               break;
+
+       default:
+               name = "Language Neutral";
+
+       }
+
+       if (!name)
+               name = "Language Neutral";
+
+       return copy_lang (lang_out, lang_len, name);
+}