From 28faf0a6d167087a259713c7278b87c6bc4f6910 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 18 Apr 2016 17:12:20 +0200 Subject: [PATCH] [tools] Add aot offsets generation tool. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit List of original contributors: Marek Habersack João Matos Zoltan Varga Rolf Bjarne Kvinge --- tools/offsets-tool/.gitignore | 5 + tools/offsets-tool/Makefile | 39 + tools/offsets-tool/MonoAotOffsetsDumper.cs | 822 +++++++++++++++++++++ 3 files changed, 866 insertions(+) create mode 100644 tools/offsets-tool/.gitignore create mode 100644 tools/offsets-tool/Makefile create mode 100644 tools/offsets-tool/MonoAotOffsetsDumper.cs diff --git a/tools/offsets-tool/.gitignore b/tools/offsets-tool/.gitignore new file mode 100644 index 00000000000..61a0f8df9c0 --- /dev/null +++ b/tools/offsets-tool/.gitignore @@ -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 index 00000000000..abf2ff924a5 --- /dev/null +++ b/tools/offsets-tool/Makefile @@ -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 index 00000000000..a5320ba473b --- /dev/null +++ b/tools/offsets-tool/MonoAotOffsetsDumper.cs @@ -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 Abis = new List (); + static string OutputDir; + + static string MonodroidDir = @""; + static string MaccoreDir = @""; + + public enum TargetPlatform + { + Android, + iOS, + WatchOS, + } + + public class Target + { + public Target() + { + Defines = new List(); + Arguments = new List(); + } + + 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 Defines; + public List Arguments; + }; + + public static List Targets = new List(); + + public static IEnumerable AndroidTargets + { + get { return Targets.Where ((t) => t.Platform == TargetPlatform.Android); } + } + + public static IEnumerable DarwinTargets + { + get + { + return Targets.Where ((t) => t.Platform == TargetPlatform.iOS || + t.Platform == TargetPlatform.WatchOS); + } + } + + public static IEnumerable 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 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 + { + "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); + } + } + } +} -- 2.25.1