Over the last week, since my Intel Galileo dev board arrived, I’ve been trying to get mono ported over to Microsoft’s “Windows on Devices”. With a bit of recent success, I wanted to share the experience and give you something to download.
tldr;Download link at the bottom of the post
Why Mono on Windows? Doesn’t Microsoft have their own CLR and why not just use a Raspberry Pi- like board?
Right now Microsoft’s Windows on IoT (let’s call it mincore until we are corrected), there is minimal .NET support. The CLR is there, but there’s little to no supporting libs. In short it’s either incomplete, or perhaps just waiting for .NET native support. I don’t want to wait around to see what Microsoft is doing with this OS. I just paid $50 for this device, so I want to do something besides blinking LEDs.
So why not just use an already Linux supported device running mono? For some people, this may be a no-brainer and probably already doing it. For others like me, we have a good chunk of dependencies on Microsoft technologies and/or APIs, which also may include our own C and C++ libraries. So naturally, something I want is a cheap low powered device I can mostly recompile and not muck up a stable codebase in the process. Mono on this mincore Windows just may fit, even if temporary.
What is this mincore-Windows-IoT and what’s involved in porting Mono over to it?
There’s little out there that really tell us the direction for this Microsoft OS. But what we can tell at this point is that its SMALL. The compressed WIM file is 171 MB! So you can imagine how much “stuff” was cut out of this. There’s no shell32.dll at all. Some Win32 functions are totally gone or moved to different DLLs. Mono already supports windows, but in the WIN32 build configuration it’s runtime and BCL use a good share of Win32 API. It should also be noted, the specific Galileo CPU doesn’t support any modern instruction set. No MMX, SSE, or the like. Luckily in this case this just came down to compiler flags.
The first step I took in trying to get mono working was simply to compile it using cygwin/mingw. I don’t have much experience with gcc, but I couldn’t get a single build of the mono runtime to execute without an “illegal instruction” error. This pointed the problem to compiler switches (-march=i586 in this case), but no matter what I tried, I got no love. On the plus side, this process did create working Mono BCLs.
I just about gave up, when I decided to see if anyone used the MSVC compiler on Mono, which is when I found out I should have RTFM from the beginning. There’s an msvc folder ready to go in their source tree, with Visual Studio .sln and project files ready to go. Changing the compiler flags to /arch:IA32 on all the projects, but also adding mincore.lib to the linker with additional options such as:
-d2:-nolock /NODEFAULTLIB:ole32.lib /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:user32.lib /NODEFAULTLIB:advapi32.lib
The linker stuff is very important. The mincore.lib appears to provide exports for moved win32 functions. For instance, CoInitializeEx used to exist in ole32.dll, but now that the dlls is gone, it’s in api-ms-win-core-com-l1-1-1.dll. the /NODEFAULTLIB:lib just tells the linker to ignore the standard win32 stuffs and use the ones defined in mincore.lib.
Figuring out what Win32 methods just are missing was another issue. At runtime, when windows loads up a DLL, it enumerates through it’s imports to verify them, and if one export is missing, you app will get a 0xC0000139, missing entry point. To find out which methods these failing at is easy.
reg ADD “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager” /v GlobalFlag /t REG_DWORD /d 2
That turns on Windows loader snaps when you debug an application after a reboot. This means running an app is very verbose, but you get the missing Win32 you were looking for. One the missing method was identified, that method usage in mono could be changed to the alternative win32 method (using the win32Ex alternative), or the calling method in the mono source was otherwise modified.
What about p/invoke stuff in the BCLs?
There’s also a good amount of DllImports in the Mono BCLs. Win32RegistryApi.cs is a good example of an internal class that is used quite a bit that will fail. They point to entry points in advapi32.dll, when they have been moved to API-MS-WIN-CORE-REGISTRY-L1-1-0.DLL. I didn’t fix every BCL DllImport yet, but did at least patch this one because how much it is used.
What isn’t working with mono besides stuff that doesn’t already work with mono?
I can’t think of much that isn’t working now that I figured out some mistakes I made early on. COM interop should be working now. Some windows security stuff may be broken. EnumProcesses doesn’t exist afaik, so that won’t work. It’s a large test surface, with a lot of variables, but hopefully it will get you by until a supported CLR comes from MS.
Where’s the code? You gotta hook it up. The power of GPL compels you!
It’s coming, I promise. The problem is I hacked up so many things, just trying to get it to work, I’m working on cleaning it up. I want it to be compatible the rest of the mono build system, only enabled with some preprocessor flags Shouldn’t be much longer than a week.
Where’s the bins, I wanna try it out!
I uploaded the latest port here: https://dl.dropboxusercontent.com/u/4165054/mono_iot.zip, Enjoy (remember, JITTing on this is SLOW)
Also remember this environment variable if you like verbose output: set MONO_LOG_LEVEL=debug
PS, Can you port Node.js to mincore for me?
No I will not.