2 using System.Collections.Generic;
5 using System.Text.RegularExpressions;
7 using CppSharp.AST.Extensions;
13 * This tool dumps the offsets of structures used in the Mono VM needed
14 * by the AOT compiler for cross-compiling code to target platforms
15 * different than the host the compiler is being invoked on.
17 * It takes two arguments: the path to your clone of the Mono repo and
18 * the path to the root of Android NDK.
20 static class MonoAotOffsetsDumper
22 static string MonoDir = @"";
24 static List<string> Abis = new List<string> ();
25 static string OutputDir;
27 static string MonodroidDir = @"";
28 static string MaccoreDir = @"";
30 public enum TargetPlatform
41 Defines = new List<string>();
42 Arguments = new List<string>();
45 public Target(Target target)
47 Platform = target.Platform;
48 Triple = target.Triple;
50 Defines = target.Defines;
51 Arguments = target.Arguments;
54 public TargetPlatform Platform;
57 public List<string> Defines;
58 public List<string> Arguments;
61 public static List<Target> Targets = new List<Target>();
63 public static IEnumerable<Target> AndroidTargets
65 get { return Targets.Where ((t) => t.Platform == TargetPlatform.Android); }
68 public static IEnumerable<Target> DarwinTargets
72 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS ||
73 t.Platform == TargetPlatform.WatchOS);
77 public static IEnumerable<Target> iOSTargets
81 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS);
85 public static void SetupAndroidTargets()
87 Targets.Add (new Target {
88 Platform = TargetPlatform.Android,
89 Triple = "i686-none-linux-android",
91 Defines = { "TARGET_X86" }
94 Targets.Add (new Target {
95 Platform = TargetPlatform.Android,
96 Triple = "x86_64-none-linux-android",
97 Build = "mono-x86_64",
98 Defines = { "TARGET_AMD64" }
101 Targets.Add (new Target {
102 Platform = TargetPlatform.Android,
103 Triple = "armv5-none-linux-androideabi",
104 Build = "mono-armv6",
105 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
108 Targets.Add (new Target {
109 Platform = TargetPlatform.Android,
110 Triple = "armv7-none-linux-androideabi",
111 Build = "mono-armv7",
112 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5", "HAVE_ARMV6",
117 Targets.Add (new Target {
118 Platform = TargetPlatform.Android,
119 Triple = "aarch64-v8a-linux-android",
120 Build = "mono-aarch64",
121 Defines = { "TARGET_ARM64" }
124 /*Targets.Add(new Target {
125 Platform = TargetPlatform.Android,
126 Triple = "mipsel-none-linux-android",
128 Defines = { "TARGET_MIPS", "__mips__" }
131 foreach (var target in AndroidTargets)
132 target.Defines.AddRange (new string[] { "PLATFORM_ANDROID",
133 "TARGET_ANDROID", "MONO_CROSS_COMPILE", "USE_MONO_CTX"
137 public static void SetupiOSTargets()
139 Targets.Add(new Target {
140 Platform = TargetPlatform.iOS,
141 Triple = "arm-apple-darwin10",
143 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
146 Targets.Add(new Target {
147 Platform = TargetPlatform.iOS,
148 Triple = "aarch64-apple-darwin10",
150 Defines = { "TARGET_ARM64" }
153 foreach (var target in iOSTargets) {
154 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
155 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
160 Targets.Add(new Target {
161 Platform = TargetPlatform.WatchOS,
162 Triple = "armv7k-apple-darwin",
163 Build = "targetwatch",
164 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
167 foreach (var target in DarwinTargets) {
168 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
169 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
175 static bool GetParentSubDirectoryPath(string parent, out string subdir)
177 var directory = Directory.GetParent(Directory.GetCurrentDirectory());
179 while (directory != null) {
180 var path = Path.Combine(directory.FullName, parent);
182 if (Directory.Exists (path)) {
187 directory = directory.Parent;
194 public static void Main(string[] args)
196 ParseCommandLineArgs(args);
199 if (!Directory.Exists (MonodroidDir) &&
200 GetParentSubDirectoryPath ("monodroid", out monodroidDir)) {
201 MonodroidDir = Path.Combine (monodroidDir);
204 if (Directory.Exists (MonodroidDir))
205 SetupAndroidTargets();
208 if (!Directory.Exists (MaccoreDir) &&
209 GetParentSubDirectoryPath ("maccore", out maccoreDir)) {
210 MaccoreDir = Path.Combine (maccoreDir);
213 if (Directory.Exists(MaccoreDir))
216 foreach (var target in Targets)
218 if (Abis.Any() && !Abis.Any (target.Triple.Contains))
222 Console.WriteLine("Processing triple: {0}", target.Triple);
224 var options = new DriverOptions();
226 var log = new TextDiagnosticPrinter();
227 var driver = new Driver(options, log);
229 Setup(driver, target);
232 BuildParseOptions(driver, target);
233 if (!driver.ParseCode())
236 Dump(driver.ASTContext, driver.TargetInfo, target);
240 static void BuildParseOptions(Driver driver, Target target)
242 foreach (var header in driver.Options.Headers)
244 var source = driver.Project.AddFile(header);
245 source.Options = driver.BuildParseOptions(source);
247 if (header.Contains ("mini"))
250 source.Options.addDefines ("HAVE_SGEN_GC");
251 source.Options.addDefines ("HAVE_MOVING_COLLECTOR");
255 static string GetAndroidNdkPath()
257 // Find the Android NDK's path from Monodroid's config.
258 var configFile = Path.Combine(MonodroidDir, "env.config");
259 if (!File.Exists(configFile))
260 throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
262 var config = File.ReadAllText(configFile);
263 var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
264 return match.Groups[1].Value.Trim();
267 static void ParseCommandLineArgs(string[] args)
269 var showHelp = false;
271 var options = new Mono.Options.OptionSet () {
272 { "abi=", "ABI triple to generate", v => Abis.Add(v) },
273 { "o|out=", "output directory", v => OutputDir = v },
274 { "maccore=", "include directory", v => MaccoreDir = v },
275 { "monodroid=", "include directory", v => MonodroidDir = v },
276 { "mono=", "include directory", v => MonoDir = v },
277 { "h|help", "show this message and exit", v => showHelp = v != null },
281 options.Parse (args);
283 catch (Mono.Options.OptionException e) {
284 Console.WriteLine (e.Message);
290 // Print usage and exit.
291 Console.WriteLine("{0} [--abi=triple] [--out=dir] "
292 + "[--monodroid/maccore=dir] [--mono=dir]",
293 AppDomain.CurrentDomain.FriendlyName);
298 static void Setup(Driver driver, Target target)
300 var options = driver.Options;
301 options.DryRun = true;
302 options.Verbose = false;
303 options.LibraryName = "Mono";
304 options.MicrosoftMode = false;
305 options.addArguments("-xc");
306 options.addArguments("-std=gnu99");
307 options.addDefines("CPPSHARP");
309 foreach (var define in target.Defines)
310 options.addDefines(define);
312 SetupToolchainPaths(driver, target);
314 SetupMono(options, target);
317 static void SetupMono(DriverOptions options, Target target)
320 switch (target.Platform) {
321 case TargetPlatform.Android:
322 targetPath = Path.Combine (MonodroidDir, "builds");
324 case TargetPlatform.WatchOS:
325 case TargetPlatform.iOS:
326 targetPath = Path.Combine (MaccoreDir, "builds");
329 throw new ArgumentOutOfRangeException ();
332 if (!Directory.Exists (MonoDir)) {
333 MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
336 var targetBuild = Path.Combine(targetPath, target.Build);
338 if (!Directory.Exists(targetBuild))
339 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
341 var includeDirs = new[]
344 Path.Combine(targetBuild, "eglib", "src"),
346 Path.Combine(MonoDir, "mono"),
347 Path.Combine(MonoDir, "mono", "mini"),
348 Path.Combine(MonoDir, "eglib", "src")
351 foreach (var inc in includeDirs)
352 options.addIncludeDirs(inc);
354 var filesToParse = new[]
356 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
357 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
360 foreach (var file in filesToParse)
361 options.Headers.Add(file);
364 static void SetupMSVC(Driver driver, string triple)
366 var options = driver.Options;
368 options.Abi = Parser.AST.CppAbi.Microsoft;
369 options.MicrosoftMode = true;
371 var systemIncludeDirs = new[]
373 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
374 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
377 foreach (var inc in systemIncludeDirs)
378 options.addSystemIncludeDirs(inc);
380 options.addDefines("HOST_WIN32");
383 static void SetupToolchainPaths(Driver driver, Target target)
385 switch (target.Platform) {
386 case TargetPlatform.Android:
387 SetupAndroidNDK(driver, target);
389 case TargetPlatform.iOS:
390 case TargetPlatform.WatchOS:
391 SetupXcode(driver, target);
394 throw new ArgumentOutOfRangeException ();
398 static string GetArchFromTriple(string triple)
400 if (triple.Contains("mips"))
403 if (triple.Contains("arm64") || triple.Contains("aarch64"))
406 if (triple.Contains("arm"))
409 if (triple.Contains("i686"))
412 if (triple.Contains("x86_64"))
415 throw new Exception("Unknown architecture from triple: " + triple);
418 static string GetXcodeToolchainPath()
420 var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
424 var toolchainPath = toolchains.LastOrDefault();
425 if (toolchainPath == null)
426 throw new Exception("Could not find a valid Xcode SDK");
428 return toolchainPath;
431 static string GetXcodeBuiltinIncludesFolder()
433 var toolchainPath = GetXcodeToolchainPath();
435 var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
436 "Contents/Developer/Toolchains")).ToList();
439 toolchainPath = toolchains.LastOrDefault();
440 if (toolchainPath == null)
441 throw new Exception("Could not find a valid Xcode toolchain");
443 var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
444 "usr/lib/clang")).ToList();
445 var includePath = includePaths.LastOrDefault();
447 if (includePath == null)
448 throw new Exception("Could not find a valid Clang include folder");
450 return Path.Combine(includePath, "include");
453 static string GetXcodeiOSIncludesFolder()
455 var toolchainPath = GetXcodeToolchainPath();
457 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
458 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
459 var sdkPath = sdkPaths.LastOrDefault();
462 throw new Exception("Could not find a valid iPhone SDK");
464 return Path.Combine(sdkPath, "usr/include");
467 static string GetXcodeWatchOSIncludesFolder()
469 var toolchainPath = GetXcodeToolchainPath();
471 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
472 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
473 var sdkPath = sdkPaths.LastOrDefault();
476 throw new Exception("Could not find a valid WatchOS SDK");
478 return Path.Combine(sdkPath, "usr/include");
481 static void SetupXcode(Driver driver, Target target)
483 var options = driver.Options;
485 var builtinsPath = GetXcodeBuiltinIncludesFolder();
488 switch (target.Platform) {
489 case TargetPlatform.iOS:
490 includePath = GetXcodeiOSIncludesFolder();
492 case TargetPlatform.WatchOS:
493 includePath = GetXcodeWatchOSIncludesFolder();
496 throw new ArgumentOutOfRangeException ();
499 options.addSystemIncludeDirs(builtinsPath);
500 options.addSystemIncludeDirs(includePath);
502 options.NoBuiltinIncludes = true;
503 options.NoStandardIncludes = true;
504 options.TargetTriple = target.Triple;
507 static string GetAndroidHostToolchainPath()
509 var androidNdkPath = GetAndroidNdkPath ();
510 var toolchains = Directory.EnumerateDirectories(
511 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
514 var toolchainPath = toolchains.LastOrDefault();
515 if (toolchainPath == null)
516 throw new Exception("Could not find a valid NDK host toolchain");
518 toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
519 "prebuilt")).ToList();
522 toolchainPath = toolchains.LastOrDefault();
523 if (toolchainPath == null)
524 throw new Exception("Could not find a valid NDK host toolchain");
526 return toolchainPath;
529 static string GetAndroidBuiltinIncludesFolder()
531 var toolchainPath = GetAndroidHostToolchainPath();
533 string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
534 if (!Directory.Exists (clangToolchainPath))
535 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
537 string includePath = null;
538 if (Directory.Exists (clangToolchainPath)) {
539 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
540 includePath = includePaths.LastOrDefault();
542 if (includePath == null)
543 throw new Exception("Could not find a valid Clang include folder");
545 return Path.Combine(includePath, "include");
548 static void SetupAndroidNDK(Driver driver, Target target)
550 var options = driver.Options;
552 var builtinsPath = GetAndroidBuiltinIncludesFolder();
553 options.addSystemIncludeDirs(builtinsPath);
555 var androidNdkRoot = GetAndroidNdkPath ();
556 const int androidNdkApiLevel = 21;
558 var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
559 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
561 options.addSystemIncludeDirs(toolchainPath);
563 options.NoBuiltinIncludes = true;
564 options.NoStandardIncludes = true;
565 options.TargetTriple = target.Triple;
568 static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
572 case ParserIntType.SignedChar:
573 case ParserIntType.UnsignedChar:
574 return target.CharAlign;
575 case ParserIntType.SignedShort:
576 case ParserIntType.UnsignedShort:
577 return target.ShortAlign;
578 case ParserIntType.SignedInt:
579 case ParserIntType.UnsignedInt:
580 return target.IntAlign;
581 case ParserIntType.SignedLong:
582 case ParserIntType.UnsignedLong:
583 return target.LongAlign;
584 case ParserIntType.SignedLongLong:
585 case ParserIntType.UnsignedLongLong:
586 return target.LongLongAlign;
588 throw new Exception("Type has no alignment");
592 static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
596 case ParserIntType.SignedChar:
597 case ParserIntType.UnsignedChar:
598 return target.CharWidth;
599 case ParserIntType.SignedShort:
600 case ParserIntType.UnsignedShort:
601 return target.ShortWidth;
602 case ParserIntType.SignedInt:
603 case ParserIntType.UnsignedInt:
604 return target.IntWidth;
605 case ParserIntType.SignedLong:
606 case ParserIntType.UnsignedLong:
607 return target.LongWidth;
608 case ParserIntType.SignedLongLong:
609 case ParserIntType.UnsignedLongLong:
610 return target.LongLongWidth;
612 throw new Exception("Type has no size");
616 static string GetTargetPlatformDefine(TargetPlatform target)
619 case TargetPlatform.Android:
620 return "TARGET_ANDROID";
621 case TargetPlatform.iOS:
623 case TargetPlatform.WatchOS:
624 return "TARGET_WATCHOS";
626 throw new ArgumentOutOfRangeException ();
630 static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
632 var targetFile = target.Triple;
634 if (!string.IsNullOrEmpty (OutputDir))
635 targetFile = Path.Combine (OutputDir, targetFile);
639 using (var writer = new StreamWriter(targetFile))
640 //using (var writer = Console.Out)
642 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
643 writer.WriteLine("#ifdef {0}", target.Defines[0]);
644 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
645 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
646 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
647 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
648 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
649 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
651 DumpAligns(writer, targetInfo);
652 DumpSizes(writer, targetInfo);
653 DumpMetadataOffsets(writer, ctx, target);
655 writer.WriteLine("#endif //disable metadata check");
657 DumpJITOffsets(writer, ctx);
659 writer.WriteLine("#endif //cross compiler checks");
660 writer.WriteLine("#endif //gc check");
661 writer.WriteLine("#endif //os check");
662 writer.WriteLine("#endif //arch check");
663 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
666 Console.WriteLine("Generated offsets file: {0}", targetFile);
669 static void DumpAligns(TextWriter writer, ParserTargetInfo target)
673 new { Name = "gint8", Align = target.CharAlign},
674 new { Name = "gint16", Align = target.ShortAlign},
675 new { Name = "gint32", Align = target.IntAlign},
676 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
677 new { Name = "float", Align = target.FloatAlign},
678 new { Name = "double", Align = target.DoubleAlign},
679 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
682 // Write the alignment info for the basic types.
683 foreach (var align in aligns)
684 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
687 static void DumpSizes(TextWriter writer, ParserTargetInfo target)
691 new { Name = "gint8", Size = target.CharWidth},
692 new { Name = "gint16", Size = target.ShortWidth},
693 new { Name = "gint32", Size = target.IntWidth},
694 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
695 new { Name = "float", Size = target.FloatWidth},
696 new { Name = "double", Size = target.DoubleWidth},
697 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
700 // Write the size info for the basic types.
701 foreach (var size in sizes)
702 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
705 static Class GetClassFromTypedef(ITypedDecl typedef)
707 var type = typedef.Type.Desugar() as TagType;
711 var @class = type.Declaration as Class;
713 return @class.IsIncomplete ?
714 (@class.CompleteDeclaration as Class) : @class;
717 static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
718 bool optional = false)
720 foreach (var @struct in types)
722 var @class = ctx.FindCompleteClass(@struct);
724 @class = ctx.FindCompleteClass("_" + @struct);
728 var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
729 decl => !decl.IsIncomplete);
732 @class = GetClassFromTypedef(typedef);
735 if (@class == null && optional)
739 throw new Exception("Expected to find struct definition for " + @struct);
741 DumpStruct(writer, @class);
745 static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
747 var types = new List<string>
753 "MonoInternalThread",
754 "MonoMulticastDelegate",
755 "MonoTransparentProxy",
762 "MonoComInteropProxy",
770 DumpClasses(writer, ctx, types);
773 static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
775 writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
776 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
781 "MonoMethodRuntimeGenericContext",
783 "MonoGSharedVtMethodRuntimeInfo",
786 "MonoDelegateTrampInfo",
789 DumpClasses(writer, ctx, types);
791 var optionalTypes = new[]
799 DumpClasses(writer, ctx, optionalTypes, optional: true);
801 writer.WriteLine("#endif //disable jit check");
804 static void DumpStruct(TextWriter writer, Class @class)
806 var name = @class.Name;
807 if (name.StartsWith ("_", StringComparison.Ordinal))
808 name = name.Substring (1);
810 foreach (var field in @class.Fields)
812 if (field.IsBitField) continue;
814 if (name == "SgenThreadInfo" && field.Name == "regs")
817 writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,