[tools] Add aot offsets generation tool.
authorRolf Bjarne Kvinge <rolf@xamarin.com>
Mon, 18 Apr 2016 15:12:20 +0000 (17:12 +0200)
committerRolf Bjarne Kvinge <rolf@xamarin.com>
Mon, 18 Apr 2016 17:26:17 +0000 (19:26 +0200)
List of original contributors:

Marek Habersack <grendel@twistedcode.net>
João Matos <joao@tritao.eu>
Zoltan Varga <vargaz@gmail.com>
Rolf Bjarne Kvinge <rolf@xamarin.com>

tools/offsets-tool/.gitignore [new file with mode: 0644]
tools/offsets-tool/Makefile [new file with mode: 0644]
tools/offsets-tool/MonoAotOffsetsDumper.cs [new file with mode: 0644]

diff --git a/tools/offsets-tool/.gitignore b/tools/offsets-tool/.gitignore
new file mode 100644 (file)
index 0000000..61a0f8d
--- /dev/null
@@ -0,0 +1,5 @@
+.stamp-clone
+CppSharp
+*.exe
+*.h
+*.exe.mdb
diff --git a/tools/offsets-tool/Makefile b/tools/offsets-tool/Makefile
new file mode 100644 (file)
index 0000000..abf2ff9
--- /dev/null
@@ -0,0 +1,39 @@
+CPPSHARP_DIR = CppSharp
+
+CPPSHARP_REFS = -r:$(CPPSHARP_DIR)/CppSharp.dll \
+       -r:$(CPPSHARP_DIR)/CppSharp.AST.dll \
+       -r:$(CPPSHARP_DIR)/CppSharp.Parser.CSharp.dll \
+       -r:$(CPPSHARP_DIR)/CppSharp.Generator.dll
+
+SRC_ROOT = ../..
+
+MONO_OPTIONS_SRC = $(SRC_ROOT)/mono/mcs/class/Mono.Options/Mono.Options/Options.cs
+
+.stamp-clone:
+       @if [ ! -d $(CPPSHARP_DIR) ]; then \
+               git clone git@github.com:xamarin/CppSharpBinaries.git $(CPPSHARP_DIR); \
+               touch $@; \
+       fi
+
+MonoAotOffsetsDumper.exe: .stamp-clone MonoAotOffsetsDumper.cs $(MONO_OPTIONS_SRC)
+       mcs MonoAotOffsetsDumper.cs /debug /nowarn:0436 $(MONO_OPTIONS_SRC) $(CPPSHARP_REFS)
+
+.PHONY: clean
+clean:
+       rm MonoAotOffsetsDumper.exe
+
+dump: MonoAotOffsetsDumper.exe 
+       MONO_PATH=$(CPPSHARP_DIR) mono MonoAotOffsetsDumper.exe
+
+update:
+       @if [ -f object-offsets.h ]; then rm object-offsets.h; fi;
+       @for f in *.h; do \
+               echo "Processing $$f.."; \
+               echo "#include \"$$f\"" >> object-offsets1.h; \
+       done
+       @cp *.h ../mono/metadata
+
+gen-proj:
+       $(CPPSHARP_DIR)/premake5 vs2012
+
+all: MonoAotOffsetsDumper.exe
\ No newline at end of file
diff --git a/tools/offsets-tool/MonoAotOffsetsDumper.cs b/tools/offsets-tool/MonoAotOffsetsDumper.cs
new file mode 100644 (file)
index 0000000..a5320ba
--- /dev/null
@@ -0,0 +1,822 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using CppSharp.AST;
+using CppSharp.AST.Extensions;
+using CppSharp.Parser;
+
+namespace CppSharp
+{
+    /**
+     * This tool dumps the offsets of structures used in the Mono VM needed
+     * by the AOT compiler for cross-compiling code to target platforms
+     * different than the host the compiler is being invoked on.
+     * 
+     * It takes two arguments: the path to your clone of the Mono repo and
+     * the path to the root of Android NDK.
+     */
+    static class MonoAotOffsetsDumper
+    {
+        static string MonoDir = @"";
+
+        static List<string> Abis = new List<string> ();
+        static string OutputDir;
+
+        static string MonodroidDir = @"";
+        static string MaccoreDir = @"";
+
+        public enum TargetPlatform
+        {
+            Android,
+            iOS,
+            WatchOS,
+        }
+
+        public class Target
+        {
+            public Target()
+            {
+                Defines = new List<string>();
+                Arguments = new List<string>();
+            }
+
+            public Target(Target target)
+            {
+                Platform = target.Platform;
+                Triple = target.Triple;
+                Build = target.Build;
+                Defines = target.Defines;
+                Arguments = target.Arguments;
+            }
+
+            public TargetPlatform Platform;
+            public string Triple;
+            public string Build;            
+            public List<string> Defines;
+            public List<string> Arguments;
+        };
+
+        public static List<Target> Targets = new List<Target>();
+
+        public static IEnumerable<Target> AndroidTargets
+        {
+            get { return Targets.Where ((t) => t.Platform == TargetPlatform.Android); }
+        }
+
+        public static IEnumerable<Target> DarwinTargets
+        {
+            get
+            {
+                return Targets.Where ((t) => t.Platform == TargetPlatform.iOS ||
+                    t.Platform == TargetPlatform.WatchOS);
+            }
+        }
+
+        public static IEnumerable<Target> iOSTargets
+        {
+            get
+            {
+                return Targets.Where ((t) => t.Platform == TargetPlatform.iOS);
+            }
+        }
+
+        public static void SetupAndroidTargets()
+        {
+            Targets.Add (new Target {
+                Platform = TargetPlatform.Android,
+                Triple = "i686-none-linux-android",
+                Build = "mono-x86",
+                Defines = { "TARGET_X86" }
+            });
+
+            Targets.Add (new Target {
+                Platform = TargetPlatform.Android,
+                Triple = "x86_64-none-linux-android",
+                Build = "mono-x86_64",
+                Defines = { "TARGET_AMD64" }
+            });            
+
+            Targets.Add (new Target {
+                Platform = TargetPlatform.Android,
+                Triple = "armv5-none-linux-androideabi",
+                Build = "mono-armv6",
+                Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
+            });
+
+            Targets.Add (new Target {
+                Platform = TargetPlatform.Android,
+                Triple = "armv7-none-linux-androideabi",
+                Build = "mono-armv7",                    
+                Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5", "HAVE_ARMV6",
+                    "HAVE_ARMV7"
+                }
+            });
+
+            Targets.Add (new Target {
+                Platform = TargetPlatform.Android,
+                Triple = "aarch64-v8a-linux-android",
+                Build = "mono-aarch64",                    
+                Defines = { "TARGET_ARM64" }
+            });            
+
+            /*Targets.Add(new Target {
+                    Platform = TargetPlatform.Android,
+                    Triple = "mipsel-none-linux-android",
+                    Build = "mono-mips",
+                    Defines = { "TARGET_MIPS", "__mips__" }
+                });*/
+
+            foreach (var target in AndroidTargets)
+                target.Defines.AddRange (new string[] { "PLATFORM_ANDROID",
+                    "TARGET_ANDROID", "MONO_CROSS_COMPILE", "USE_MONO_CTX"
+                });
+        }
+
+        public static void SetupiOSTargets()
+        {
+            Targets.Add(new Target {
+                Platform = TargetPlatform.iOS,
+                Triple = "arm-apple-darwin10",
+                Build = "target7",
+                Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
+            });
+
+            Targets.Add(new Target {
+                Platform = TargetPlatform.iOS,
+                Triple = "aarch64-apple-darwin10",
+                Build = "target64",                    
+                Defines = { "TARGET_ARM64" }
+            });
+
+            foreach (var target in iOSTargets) {
+                target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
+                    "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
+                    "_XOPEN_SOURCE"
+                });
+            }
+
+            Targets.Add(new Target {
+                Platform = TargetPlatform.WatchOS,
+                Triple = "armv7k-apple-darwin",
+                Build = "targetwatch",
+                Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
+            });
+
+            foreach (var target in DarwinTargets) {
+                target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
+                    "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
+                    "_XOPEN_SOURCE"
+                });
+            }
+        }
+
+        static bool GetParentSubDirectoryPath(string parent, out string subdir)
+        {
+            var directory = Directory.GetParent(Directory.GetCurrentDirectory());
+
+            while (directory != null) {
+                var path = Path.Combine(directory.FullName, parent);
+
+                if (Directory.Exists (path)) {
+                    subdir = path;
+                    return true;
+                }
+
+                directory = directory.Parent;
+            }
+
+            subdir = null;
+            return false;
+        }
+
+        public static void Main(string[] args)
+        {
+            ParseCommandLineArgs(args);
+
+            string monodroidDir;
+            if (!Directory.Exists (MonodroidDir) &&
+                GetParentSubDirectoryPath ("monodroid", out monodroidDir)) {
+                MonodroidDir = Path.Combine (monodroidDir);
+            }
+
+            if (Directory.Exists (MonodroidDir))
+                SetupAndroidTargets();
+
+            string maccoreDir;
+            if (!Directory.Exists (MaccoreDir) &&
+                GetParentSubDirectoryPath ("maccore", out maccoreDir)) {
+                MaccoreDir = Path.Combine (maccoreDir);
+            }
+
+            if (Directory.Exists(MaccoreDir))
+                SetupiOSTargets();
+
+            foreach (var target in Targets)
+             {
+                if (Abis.Any() && !Abis.Any (target.Triple.Contains))
+                    continue;
+                
+                Console.WriteLine();
+                Console.WriteLine("Processing triple: {0}", target.Triple);
+
+                var options = new DriverOptions();
+
+                var log = new TextDiagnosticPrinter();
+                var driver = new Driver(options, log);
+
+                Setup(driver, target);
+                driver.Setup();
+
+                BuildParseOptions(driver, target);
+                if (!driver.ParseCode())
+                    return;
+
+                Dump(driver.ASTContext, driver.TargetInfo, target);
+            }
+        }
+
+        static void BuildParseOptions(Driver driver, Target target)
+        {
+            foreach (var header in driver.Options.Headers)
+            {
+                var source = driver.Project.AddFile(header);
+                source.Options = driver.BuildParseOptions(source);
+
+                if (header.Contains ("mini"))
+                    continue;
+
+                source.Options.addDefines ("HAVE_SGEN_GC");
+                source.Options.addDefines ("HAVE_MOVING_COLLECTOR");
+            }
+        }
+
+        static string GetAndroidNdkPath()
+        {
+            // Find the Android NDK's path from Monodroid's config.
+            var configFile = Path.Combine(MonodroidDir, "env.config");
+            if (!File.Exists(configFile))
+                throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
+
+            var config = File.ReadAllText(configFile);
+            var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
+            return match.Groups[1].Value.Trim();
+        }
+
+        static void ParseCommandLineArgs(string[] args)
+        {
+            var showHelp = false;
+
+            var options = new Mono.Options.OptionSet () {
+                { "abi=", "ABI triple to generate", v => Abis.Add(v) },
+                { "o|out=", "output directory", v => OutputDir = v },
+                { "maccore=", "include directory", v => MaccoreDir = v },
+                { "monodroid=", "include directory", v => MonodroidDir = v },
+                { "mono=", "include directory", v => MonoDir = v },
+                { "h|help",  "show this message and exit",  v => showHelp = v != null },
+            };
+
+            try {
+                options.Parse (args);
+            }
+            catch (Mono.Options.OptionException e) {
+                Console.WriteLine (e.Message);
+                Environment.Exit(0);
+            }
+
+            if (showHelp)
+            {
+                // Print usage and exit.
+                Console.WriteLine("{0} [--abi=triple] [--out=dir] "
+                    + "[--monodroid/maccore=dir] [--mono=dir]",
+                    AppDomain.CurrentDomain.FriendlyName);
+                Environment.Exit(0);
+            }
+        }
+
+        static void Setup(Driver driver, Target target)
+        {
+            var options = driver.Options;
+            options.DryRun = true;
+            options.Verbose = false;
+            options.LibraryName = "Mono";
+            options.MicrosoftMode = false;
+            options.addArguments("-xc");
+            options.addArguments("-std=gnu99");
+            options.addDefines("CPPSHARP");
+
+            foreach (var define in target.Defines)
+                options.addDefines(define);
+
+            SetupToolchainPaths(driver, target);
+
+            SetupMono(options, target);
+        }
+
+        static void SetupMono(DriverOptions options, Target target)
+        {
+            string targetPath;
+            switch (target.Platform) {
+            case TargetPlatform.Android:
+                targetPath = Path.Combine (MonodroidDir, "builds");
+                break;
+            case TargetPlatform.WatchOS:
+            case TargetPlatform.iOS:
+                targetPath = Path.Combine (MaccoreDir, "builds");
+                break;
+            default:
+                throw new ArgumentOutOfRangeException ();
+            }
+
+            if (!Directory.Exists (MonoDir)) {
+                MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
+            }
+
+            var targetBuild = Path.Combine(targetPath, target.Build);
+
+            if (!Directory.Exists(targetBuild))
+                throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
+
+            var includeDirs = new[]
+            {
+                targetBuild,
+                Path.Combine(targetBuild, "eglib", "src"),
+                MonoDir,
+                Path.Combine(MonoDir, "mono"),
+                Path.Combine(MonoDir, "mono", "mini"),
+                Path.Combine(MonoDir, "eglib", "src")
+            };
+
+            foreach (var inc in includeDirs)
+                options.addIncludeDirs(inc);
+
+            var filesToParse = new[]
+            {
+                Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
+                Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
+            };
+
+            foreach (var file in filesToParse)
+                options.Headers.Add(file);
+        }
+
+        static void SetupMSVC(Driver driver, string triple)
+        {
+            var options = driver.Options;
+
+            options.Abi = Parser.AST.CppAbi.Microsoft;
+            options.MicrosoftMode = true;
+
+            var systemIncludeDirs = new[]
+            {
+                @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
+                @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
+            };
+
+            foreach (var inc in systemIncludeDirs)
+                options.addSystemIncludeDirs(inc);
+
+            options.addDefines("HOST_WIN32");
+        }
+
+        static void SetupToolchainPaths(Driver driver, Target target)
+        {
+            switch (target.Platform) {
+            case TargetPlatform.Android:
+                SetupAndroidNDK(driver, target);
+                break;
+            case TargetPlatform.iOS:
+            case TargetPlatform.WatchOS:
+                SetupXcode(driver, target);
+                break;
+            default:
+                throw new ArgumentOutOfRangeException ();
+            }
+        }        
+
+        static string GetArchFromTriple(string triple)
+        {
+            if (triple.Contains("mips"))
+                return "mips";
+
+            if (triple.Contains("arm64") || triple.Contains("aarch64"))
+                return "arm64";
+
+            if (triple.Contains("arm"))
+                return "arm";
+
+            if (triple.Contains("i686"))
+                return "x86";
+
+            if (triple.Contains("x86_64"))
+                return "x86_64";
+
+            throw  new Exception("Unknown architecture from triple: " + triple);
+        }
+
+        static string GetXcodeToolchainPath()
+        {
+            var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
+                .ToList();
+            toolchains.Sort();
+
+            var toolchainPath = toolchains.LastOrDefault();
+            if (toolchainPath == null)
+                throw new Exception("Could not find a valid Xcode SDK");
+
+            return toolchainPath;
+        }
+
+        static string GetXcodeBuiltinIncludesFolder()
+        {
+            var toolchainPath = GetXcodeToolchainPath();
+
+            var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
+                "Contents/Developer/Toolchains")).ToList();
+            toolchains.Sort();
+
+            toolchainPath = toolchains.LastOrDefault();
+            if (toolchainPath == null)
+                throw new Exception("Could not find a valid Xcode toolchain");
+
+            var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
+                "usr/lib/clang")).ToList();
+            var includePath = includePaths.LastOrDefault();
+
+            if (includePath == null)
+                throw new Exception("Could not find a valid Clang include folder");
+
+            return Path.Combine(includePath, "include");
+        }
+
+        static string GetXcodeiOSIncludesFolder()
+        {
+            var toolchainPath = GetXcodeToolchainPath();
+
+            var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
+                "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
+            var sdkPath = sdkPaths.LastOrDefault();
+
+            if (sdkPath == null)
+                throw new Exception("Could not find a valid iPhone SDK");
+
+            return Path.Combine(sdkPath, "usr/include");
+        }
+
+        static string GetXcodeWatchOSIncludesFolder()
+        {
+            var toolchainPath = GetXcodeToolchainPath();
+
+            var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
+                "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
+            var sdkPath = sdkPaths.LastOrDefault();
+
+            if (sdkPath == null)
+                throw new Exception("Could not find a valid WatchOS SDK");
+
+            return Path.Combine(sdkPath, "usr/include");
+        }
+
+        static void SetupXcode(Driver driver, Target target)
+        {
+            var options = driver.Options;
+
+            var builtinsPath = GetXcodeBuiltinIncludesFolder();
+            string includePath;
+
+            switch (target.Platform) {
+            case TargetPlatform.iOS:
+                includePath = GetXcodeiOSIncludesFolder();
+                break;
+            case TargetPlatform.WatchOS:
+                includePath = GetXcodeWatchOSIncludesFolder();
+                break;
+            default:
+                throw new ArgumentOutOfRangeException ();
+            }
+
+            options.addSystemIncludeDirs(builtinsPath);
+            options.addSystemIncludeDirs(includePath);
+
+            options.NoBuiltinIncludes = true;
+            options.NoStandardIncludes = true;
+            options.TargetTriple = target.Triple;
+        }
+
+        static string GetAndroidHostToolchainPath()
+        {
+            var androidNdkPath = GetAndroidNdkPath ();
+            var toolchains = Directory.EnumerateDirectories(
+                Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
+            toolchains.Sort();
+
+            var toolchainPath = toolchains.LastOrDefault();
+            if (toolchainPath == null)
+                throw new Exception("Could not find a valid NDK host toolchain");
+
+            toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
+                "prebuilt")).ToList();
+            toolchains.Sort();
+
+            toolchainPath = toolchains.LastOrDefault();
+            if (toolchainPath == null)
+                throw new Exception("Could not find a valid NDK host toolchain");
+
+            return toolchainPath;
+        }
+
+        static string GetAndroidBuiltinIncludesFolder()
+        {
+            var toolchainPath = GetAndroidHostToolchainPath();
+
+            string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
+            if (!Directory.Exists (clangToolchainPath))
+                clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
+
+            string includePath = null;
+            if (Directory.Exists (clangToolchainPath)) {
+                var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
+                includePath = includePaths.LastOrDefault();
+            }
+            if (includePath == null)
+                throw new Exception("Could not find a valid Clang include folder");
+
+            return Path.Combine(includePath, "include");
+        }
+
+        static void SetupAndroidNDK(Driver driver, Target target)
+        {
+            var options = driver.Options;
+
+            var builtinsPath = GetAndroidBuiltinIncludesFolder();
+            options.addSystemIncludeDirs(builtinsPath);
+
+            var androidNdkRoot = GetAndroidNdkPath ();
+            const int androidNdkApiLevel = 21;
+
+            var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
+                "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
+                "usr", "include");
+            options.addSystemIncludeDirs(toolchainPath);
+
+            options.NoBuiltinIncludes = true;
+            options.NoStandardIncludes = true;
+            options.TargetTriple = target.Triple;
+        }
+
+        static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
+        {
+            switch (type)
+            {
+                case ParserIntType.SignedChar:
+                case ParserIntType.UnsignedChar:
+                    return target.CharAlign;
+                case ParserIntType.SignedShort:
+                case ParserIntType.UnsignedShort:
+                    return target.ShortAlign;
+                case ParserIntType.SignedInt:
+                case ParserIntType.UnsignedInt:
+                    return target.IntAlign;
+                case ParserIntType.SignedLong:
+                case ParserIntType.UnsignedLong:
+                    return target.LongAlign;
+                case ParserIntType.SignedLongLong:
+                case ParserIntType.UnsignedLongLong:
+                    return target.LongLongAlign;
+                default:
+                    throw new Exception("Type has no alignment");
+            }
+        }
+
+        static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
+        {
+            switch (type)
+            {
+                case ParserIntType.SignedChar:
+                case ParserIntType.UnsignedChar:
+                    return target.CharWidth;
+                case ParserIntType.SignedShort:
+                case ParserIntType.UnsignedShort:
+                    return target.ShortWidth;
+                case ParserIntType.SignedInt:
+                case ParserIntType.UnsignedInt:
+                    return target.IntWidth;
+                case ParserIntType.SignedLong:
+                case ParserIntType.UnsignedLong:
+                    return target.LongWidth;
+                case ParserIntType.SignedLongLong:
+                case ParserIntType.UnsignedLongLong:
+                    return target.LongLongWidth;
+                default:
+                    throw new Exception("Type has no size");
+            }
+        }
+
+        static string GetTargetPlatformDefine(TargetPlatform target)
+        {
+            switch (target) {
+            case TargetPlatform.Android:
+                return "TARGET_ANDROID";
+            case TargetPlatform.iOS:
+                return "TARGET_IOS";
+            case TargetPlatform.WatchOS:
+                return "TARGET_WATCHOS";
+            default:
+                throw new ArgumentOutOfRangeException ();
+            }
+        }
+
+        static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
+        {
+            var targetFile = target.Triple;
+
+                       if (!string.IsNullOrEmpty (OutputDir))
+                               targetFile = Path.Combine (OutputDir, targetFile);
+
+            targetFile += ".h";
+
+            using (var writer = new StreamWriter(targetFile))
+            //using (var writer = Console.Out)
+            {
+                writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
+                writer.WriteLine("#ifdef {0}", target.Defines[0]);
+                writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
+                writer.WriteLine("#ifndef HAVE_BOEHM_GC");
+                writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
+                writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
+                writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
+                writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
+
+                DumpAligns(writer, targetInfo);
+                DumpSizes(writer, targetInfo);
+                DumpMetadataOffsets(writer, ctx, target);
+
+                writer.WriteLine("#endif //disable metadata check");
+
+                DumpJITOffsets(writer, ctx);
+
+                writer.WriteLine("#endif //cross compiler checks");
+                writer.WriteLine("#endif //gc check");
+                writer.WriteLine("#endif //os check");
+                writer.WriteLine("#endif //arch check");
+                writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
+            }
+
+            Console.WriteLine("Generated offsets file: {0}", targetFile);
+        }
+
+        static void DumpAligns(TextWriter writer, ParserTargetInfo target)
+        {
+            var aligns = new[]
+            {
+                new { Name = "gint8", Align = target.CharAlign},
+                new { Name = "gint16", Align = target.ShortAlign},
+                new { Name = "gint32", Align = target.IntAlign},
+                new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
+                new { Name = "float", Align = target.FloatAlign},
+                new { Name = "double", Align = target.DoubleAlign},
+                new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
+            };
+
+            // Write the alignment info for the basic types.
+            foreach (var align in aligns)
+                writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
+        }
+
+        static void DumpSizes(TextWriter writer, ParserTargetInfo target)
+        {
+            var sizes = new[]
+            {
+                new { Name = "gint8", Size = target.CharWidth},
+                new { Name = "gint16", Size = target.ShortWidth},
+                new { Name = "gint32", Size = target.IntWidth},
+                new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
+                new { Name = "float", Size = target.FloatWidth},
+                new { Name = "double", Size = target.DoubleWidth},
+                new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
+            };
+
+            // Write the size info for the basic types.
+            foreach (var size in sizes)
+                writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
+        }
+
+        static Class GetClassFromTypedef(ITypedDecl typedef)
+        {
+            var type = typedef.Type.Desugar() as TagType;
+            if (type == null)
+                return null;
+
+            var @class = type.Declaration as Class;
+
+            return @class.IsIncomplete ?
+                (@class.CompleteDeclaration as Class) : @class; 
+        }
+
+        static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
+            bool optional = false)
+        {
+            foreach (var @struct in types)
+            {
+                var @class = ctx.FindCompleteClass(@struct);
+                if (@class == null)
+                    @class = ctx.FindCompleteClass("_" + @struct);
+
+                if (@class == null)
+                {
+                    var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
+                        decl => !decl.IsIncomplete);
+
+                    if (typedef != null)
+                        @class = GetClassFromTypedef(typedef);
+                }
+
+                if (@class == null && optional)
+                    continue;
+
+                if (@class == null)
+                    throw new Exception("Expected to find struct definition for " + @struct);
+
+                DumpStruct(writer, @class);
+            }
+        }
+
+        static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
+        {
+            var types = new List<string>
+            {
+                "MonoObject",
+                "MonoClass",
+                "MonoVTable",
+                "MonoDelegate",
+                "MonoInternalThread",
+                "MonoMulticastDelegate",
+                "MonoTransparentProxy",
+                "MonoRealProxy",
+                "MonoRemoteClass",
+                "MonoArray",
+                "MonoArrayBounds",
+                "MonoSafeHandle",
+                "MonoHandleRef",
+                "MonoComInteropProxy",
+                "MonoString",
+                "MonoException",
+                "MonoTypedRef",
+                "MonoThreadsSync",
+                "SgenThreadInfo"
+            };
+
+            DumpClasses(writer, ctx, types);
+        }
+
+        static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
+        {
+            writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
+            writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
+
+            var types = new[]
+            {
+                "MonoLMF",
+                "MonoMethodRuntimeGenericContext",
+                "MonoJitTlsData",
+                "MonoGSharedVtMethodRuntimeInfo",
+                "MonoContinuation",
+                "MonoContext",
+                "MonoDelegateTrampInfo",
+            };
+
+            DumpClasses(writer, ctx, types);
+
+            var optionalTypes = new[]
+            {
+                "GSharedVtCallInfo",
+                "SeqPointInfo",
+                "DynCallArgs", 
+                "MonoLMFTramp",
+            };            
+
+            DumpClasses(writer, ctx, optionalTypes, optional: true);
+
+            writer.WriteLine("#endif //disable jit check");
+        }
+
+        static void DumpStruct(TextWriter writer, Class @class)
+        {
+            var name = @class.Name;
+                       if (name.StartsWith ("_", StringComparison.Ordinal))
+                               name = name.Substring (1);
+
+            foreach (var field in @class.Fields)
+            {
+                if (field.IsBitField) continue;
+
+                if (name == "SgenThreadInfo" && field.Name == "regs")
+                    continue;
+
+                writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,
+                    field.Offset / 8);
+            }
+        }
+    }
+}