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 bool XamarinAndroid;
28 static string MonodroidDir = @"";
29 static string AndroidNdkPath = @"";
30 static string MaccoreDir = @"";
32 public enum TargetPlatform
43 Defines = new List<string>();
44 Arguments = new List<string>();
47 public Target(Target target)
49 Platform = target.Platform;
50 Triple = target.Triple;
52 Defines = target.Defines;
53 Arguments = target.Arguments;
56 public TargetPlatform Platform;
59 public List<string> Defines;
60 public List<string> Arguments;
63 public static List<Target> Targets = new List<Target>();
65 public static IEnumerable<Target> AndroidTargets
67 get { return Targets.Where ((t) => t.Platform == TargetPlatform.Android); }
70 public static IEnumerable<Target> DarwinTargets
74 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS ||
75 t.Platform == TargetPlatform.WatchOS);
79 public static IEnumerable<Target> iOSTargets
83 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS);
87 public static void SetupAndroidTargets()
89 Targets.Add (new Target {
90 Platform = TargetPlatform.Android,
91 Triple = "i686-none-linux-android",
92 Build = XamarinAndroid ? "x86" : "mono-x86",
93 Defines = { "TARGET_X86" }
96 Targets.Add (new Target {
97 Platform = TargetPlatform.Android,
98 Triple = "x86_64-none-linux-android",
99 Build = XamarinAndroid ? "x86_64" : "mono-x86_64",
100 Defines = { "TARGET_AMD64" }
103 Targets.Add (new Target {
104 Platform = TargetPlatform.Android,
105 Triple = "armv5-none-linux-androideabi",
106 Build = XamarinAndroid ? "armeabi" : "mono-armv6",
107 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
110 Targets.Add (new Target {
111 Platform = TargetPlatform.Android,
112 Triple = "armv7-none-linux-androideabi",
113 Build = XamarinAndroid ? "armeabi-v7a" : "mono-armv7",
114 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5", "HAVE_ARMV6",
119 Targets.Add (new Target {
120 Platform = TargetPlatform.Android,
121 Triple = "aarch64-v8a-linux-android",
122 Build = XamarinAndroid ? "arm64-v8a" : "mono-aarch64",
123 Defines = { "TARGET_ARM64" }
126 /*Targets.Add(new Target {
127 Platform = TargetPlatform.Android,
128 Triple = "mipsel-none-linux-android",
130 Defines = { "TARGET_MIPS", "__mips__" }
133 foreach (var target in AndroidTargets)
134 target.Defines.AddRange (new string[] { "PLATFORM_ANDROID",
135 "TARGET_ANDROID", "MONO_CROSS_COMPILE", "USE_MONO_CTX"
139 public static void SetupiOSTargets()
141 Targets.Add(new Target {
142 Platform = TargetPlatform.iOS,
143 Triple = "arm-apple-darwin10",
145 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
148 Targets.Add(new Target {
149 Platform = TargetPlatform.iOS,
150 Triple = "aarch64-apple-darwin10",
152 Defines = { "TARGET_ARM64" }
155 foreach (var target in iOSTargets) {
156 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
157 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
162 Targets.Add(new Target {
163 Platform = TargetPlatform.WatchOS,
164 Triple = "armv7k-apple-darwin",
165 Build = "targetwatch",
166 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
169 foreach (var target in DarwinTargets) {
170 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
171 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
177 static bool GetParentSubDirectoryPath(string parent, out string subdir)
179 var directory = Directory.GetParent(Directory.GetCurrentDirectory());
181 while (directory != null) {
182 var path = Path.Combine(directory.FullName, parent);
184 if (Directory.Exists (path)) {
189 directory = directory.Parent;
196 public static void Main(string[] args)
198 ParseCommandLineArgs(args);
201 if (!Directory.Exists (MonodroidDir) &&
202 GetParentSubDirectoryPath ("monodroid", out monodroidDir)) {
203 MonodroidDir = Path.Combine (monodroidDir);
206 if (Directory.Exists (MonodroidDir))
207 SetupAndroidTargets();
210 if (!Directory.Exists (MaccoreDir) &&
211 GetParentSubDirectoryPath ("maccore", out maccoreDir)) {
212 MaccoreDir = Path.Combine (maccoreDir);
215 if (Directory.Exists(MaccoreDir))
218 foreach (var target in Targets)
220 if (Abis.Any() && !Abis.Any (target.Triple.Contains))
224 Console.WriteLine("Processing triple: {0}", target.Triple);
226 var options = new DriverOptions();
228 var log = new TextDiagnosticPrinter();
229 var driver = new Driver(options, log);
231 Setup(driver, target);
234 BuildParseOptions(driver, target);
235 if (!driver.ParseCode())
238 Dump(driver.Context.ASTContext, driver.Context.TargetInfo, target);
242 static void BuildParseOptions(Driver driver, Target target)
244 foreach (var header in driver.Options.Headers)
246 var source = driver.Project.AddFile(header);
247 source.Options = driver.BuildParserOptions(source);
249 if (header.Contains ("mini"))
252 source.Options.AddDefines ("HAVE_SGEN_GC");
253 source.Options.AddDefines ("HAVE_MOVING_COLLECTOR");
257 static string GetAndroidNdkPath()
259 if (!String.IsNullOrEmpty (AndroidNdkPath))
260 return AndroidNdkPath;
262 // Find the Android NDK's path from Monodroid's config.
263 var configFile = Path.Combine(MonodroidDir, "env.config");
264 if (!File.Exists(configFile))
265 throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
267 var config = File.ReadAllText(configFile);
268 var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
269 return match.Groups[1].Value.Trim();
272 static void ParseCommandLineArgs(string[] args)
274 var showHelp = false;
276 var options = new Mono.Options.OptionSet () {
277 { "abi=", "ABI triple to generate", v => Abis.Add(v) },
278 { "o|out=", "output directory", v => OutputDir = v },
279 { "maccore=", "include directory", v => MaccoreDir = v },
280 { "monodroid=", "top monodroid directory", v => MonodroidDir = v },
281 { "android-ndk=", "Path to Android NDK", v => AndroidNdkPath = v },
282 { "xamarin-android", "Generate for Xamarin.Android instead of monodroid", v => XamarinAndroid = true },
283 { "mono=", "include directory", v => MonoDir = v },
284 { "h|help", "show this message and exit", v => showHelp = v != null },
288 options.Parse (args);
290 catch (Mono.Options.OptionException e) {
291 Console.WriteLine (e.Message);
297 // Print usage and exit.
298 Console.WriteLine("{0} [--abi=triple] [--out=dir] "
299 + "[--monodroid/maccore=dir] [--mono=dir]",
300 AppDomain.CurrentDomain.FriendlyName);
305 static void Setup(Driver driver, Target target)
307 var options = driver.Options;
308 options.DryRun = true;
309 options.LibraryName = "Mono";
311 var parserOptions = driver.ParserOptions;
312 parserOptions.Verbose = false;
313 parserOptions.MicrosoftMode = false;
314 parserOptions.AddArguments("-xc");
315 parserOptions.AddArguments("-std=gnu99");
316 parserOptions.AddDefines("CPPSHARP");
318 foreach (var define in target.Defines)
319 parserOptions.AddDefines(define);
321 SetupToolchainPaths(driver, target);
323 SetupMono(driver, target);
326 static void SetupMono(Driver driver, Target target)
329 switch (target.Platform) {
330 case TargetPlatform.Android:
331 targetPath = Path.Combine (MonodroidDir, XamarinAndroid ? "build-tools/mono-runtimes/obj/Debug" : "builds");
333 case TargetPlatform.WatchOS:
334 case TargetPlatform.iOS:
335 targetPath = Path.Combine (MaccoreDir, "builds");
338 throw new ArgumentOutOfRangeException ();
341 if (!Directory.Exists (MonoDir)) {
342 MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
345 var targetBuild = Path.Combine(targetPath, target.Build);
347 if (!Directory.Exists(targetBuild))
348 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
350 var includeDirs = new[]
353 Path.Combine(targetBuild, "eglib", "src"),
355 Path.Combine(MonoDir, "mono"),
356 Path.Combine(MonoDir, "mono", "mini"),
357 Path.Combine(MonoDir, "eglib", "src")
360 foreach (var inc in includeDirs)
361 driver.ParserOptions.AddIncludeDirs(inc);
363 var filesToParse = new[]
365 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
366 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
369 foreach (var file in filesToParse)
370 driver.Options.Headers.Add(file);
373 static void SetupMSVC(Driver driver, string triple)
375 var parserOptions = driver.ParserOptions;
377 parserOptions.Abi = Parser.AST.CppAbi.Microsoft;
378 parserOptions.MicrosoftMode = true;
380 var systemIncludeDirs = new[]
382 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
383 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
386 foreach (var inc in systemIncludeDirs)
387 parserOptions.AddSystemIncludeDirs(inc);
389 parserOptions.AddDefines("HOST_WIN32");
392 static void SetupToolchainPaths(Driver driver, Target target)
394 switch (target.Platform) {
395 case TargetPlatform.Android:
396 SetupAndroidNDK(driver, target);
398 case TargetPlatform.iOS:
399 case TargetPlatform.WatchOS:
400 SetupXcode(driver, target);
403 throw new ArgumentOutOfRangeException ();
407 static string GetArchFromTriple(string triple)
409 if (triple.Contains("mips"))
412 if (triple.Contains("arm64") || triple.Contains("aarch64"))
415 if (triple.Contains("arm"))
418 if (triple.Contains("i686"))
421 if (triple.Contains("x86_64"))
424 throw new Exception("Unknown architecture from triple: " + triple);
427 static string GetXcodeToolchainPath()
429 var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
433 var toolchainPath = toolchains.LastOrDefault();
434 if (toolchainPath == null)
435 throw new Exception("Could not find a valid Xcode SDK");
437 return toolchainPath;
440 static string GetXcodeBuiltinIncludesFolder()
442 var toolchainPath = GetXcodeToolchainPath();
444 var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
445 "Contents/Developer/Toolchains")).ToList();
448 toolchainPath = toolchains.LastOrDefault();
449 if (toolchainPath == null)
450 throw new Exception("Could not find a valid Xcode toolchain");
452 var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
453 "usr/lib/clang")).ToList();
454 var includePath = includePaths.LastOrDefault();
456 if (includePath == null)
457 throw new Exception("Could not find a valid Clang include folder");
459 return Path.Combine(includePath, "include");
462 static string GetXcodeiOSIncludesFolder()
464 var toolchainPath = GetXcodeToolchainPath();
466 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
467 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
468 var sdkPath = sdkPaths.LastOrDefault();
471 throw new Exception("Could not find a valid iPhone SDK");
473 return Path.Combine(sdkPath, "usr/include");
476 static string GetXcodeWatchOSIncludesFolder()
478 var toolchainPath = GetXcodeToolchainPath();
480 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
481 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
482 var sdkPath = sdkPaths.LastOrDefault();
485 throw new Exception("Could not find a valid WatchOS SDK");
487 return Path.Combine(sdkPath, "usr/include");
490 static void SetupXcode(Driver driver, Target target)
492 var parserOptions = driver.ParserOptions;
494 var builtinsPath = GetXcodeBuiltinIncludesFolder();
497 switch (target.Platform) {
498 case TargetPlatform.iOS:
499 includePath = GetXcodeiOSIncludesFolder();
501 case TargetPlatform.WatchOS:
502 includePath = GetXcodeWatchOSIncludesFolder();
505 throw new ArgumentOutOfRangeException ();
508 parserOptions.AddSystemIncludeDirs(builtinsPath);
509 parserOptions.AddSystemIncludeDirs(includePath);
511 parserOptions.NoBuiltinIncludes = true;
512 parserOptions.NoStandardIncludes = true;
513 parserOptions.TargetTriple = target.Triple;
516 static string GetAndroidHostToolchainPath()
518 var androidNdkPath = GetAndroidNdkPath ();
519 var toolchains = Directory.EnumerateDirectories(
520 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
523 var toolchainPath = toolchains.LastOrDefault();
524 if (toolchainPath == null)
525 throw new Exception("Could not find a valid NDK host toolchain");
527 toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
528 "prebuilt")).ToList();
531 toolchainPath = toolchains.LastOrDefault();
532 if (toolchainPath == null)
533 throw new Exception("Could not find a valid NDK host toolchain");
535 return toolchainPath;
538 static string GetAndroidBuiltinIncludesFolder()
540 var toolchainPath = GetAndroidHostToolchainPath();
542 string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
543 if (!Directory.Exists (clangToolchainPath))
544 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
546 string includePath = null;
547 if (Directory.Exists (clangToolchainPath)) {
548 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
549 includePath = includePaths.LastOrDefault();
551 if (includePath == null)
552 throw new Exception("Could not find a valid Clang include folder");
554 return Path.Combine(includePath, "include");
557 static void SetupAndroidNDK(Driver driver, Target target)
559 var options = driver.Options;
560 var parserOptions = driver.ParserOptions;
562 var builtinsPath = GetAndroidBuiltinIncludesFolder();
563 parserOptions.AddSystemIncludeDirs(builtinsPath);
565 var androidNdkRoot = GetAndroidNdkPath ();
566 const int androidNdkApiLevel = 21;
568 var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
569 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
571 parserOptions.AddSystemIncludeDirs(toolchainPath);
573 parserOptions.NoBuiltinIncludes = true;
574 parserOptions.NoStandardIncludes = true;
575 parserOptions.TargetTriple = target.Triple;
578 static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
582 case ParserIntType.SignedChar:
583 case ParserIntType.UnsignedChar:
584 return target.CharAlign;
585 case ParserIntType.SignedShort:
586 case ParserIntType.UnsignedShort:
587 return target.ShortAlign;
588 case ParserIntType.SignedInt:
589 case ParserIntType.UnsignedInt:
590 return target.IntAlign;
591 case ParserIntType.SignedLong:
592 case ParserIntType.UnsignedLong:
593 return target.LongAlign;
594 case ParserIntType.SignedLongLong:
595 case ParserIntType.UnsignedLongLong:
596 return target.LongLongAlign;
598 throw new Exception("Type has no alignment");
602 static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
606 case ParserIntType.SignedChar:
607 case ParserIntType.UnsignedChar:
608 return target.CharWidth;
609 case ParserIntType.SignedShort:
610 case ParserIntType.UnsignedShort:
611 return target.ShortWidth;
612 case ParserIntType.SignedInt:
613 case ParserIntType.UnsignedInt:
614 return target.IntWidth;
615 case ParserIntType.SignedLong:
616 case ParserIntType.UnsignedLong:
617 return target.LongWidth;
618 case ParserIntType.SignedLongLong:
619 case ParserIntType.UnsignedLongLong:
620 return target.LongLongWidth;
622 throw new Exception("Type has no size");
626 static string GetTargetPlatformDefine(TargetPlatform target)
629 case TargetPlatform.Android:
630 return "TARGET_ANDROID";
631 case TargetPlatform.iOS:
633 case TargetPlatform.WatchOS:
634 return "TARGET_WATCHOS";
636 throw new ArgumentOutOfRangeException ();
640 static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
642 var targetFile = target.Triple;
644 if (!string.IsNullOrEmpty (OutputDir))
645 targetFile = Path.Combine (OutputDir, targetFile);
649 using (var writer = new StreamWriter(targetFile))
650 //using (var writer = Console.Out)
652 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
653 writer.WriteLine("#ifdef {0}", target.Defines[0]);
654 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
655 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
656 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
657 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
658 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
659 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
661 DumpAligns(writer, targetInfo);
662 DumpSizes(writer, targetInfo);
663 DumpMetadataOffsets(writer, ctx, target);
665 writer.WriteLine("#endif //disable metadata check");
667 DumpJITOffsets(writer, ctx);
669 writer.WriteLine("#endif //cross compiler checks");
670 writer.WriteLine("#endif //gc check");
671 writer.WriteLine("#endif //os check");
672 writer.WriteLine("#endif //arch check");
673 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
676 Console.WriteLine("Generated offsets file: {0}", targetFile);
679 static void DumpAligns(TextWriter writer, ParserTargetInfo target)
683 new { Name = "gint8", Align = target.CharAlign},
684 new { Name = "gint16", Align = target.ShortAlign},
685 new { Name = "gint32", Align = target.IntAlign},
686 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
687 new { Name = "float", Align = target.FloatAlign},
688 new { Name = "double", Align = target.DoubleAlign},
689 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
692 // Write the alignment info for the basic types.
693 foreach (var align in aligns)
694 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
697 static void DumpSizes(TextWriter writer, ParserTargetInfo target)
701 new { Name = "gint8", Size = target.CharWidth},
702 new { Name = "gint16", Size = target.ShortWidth},
703 new { Name = "gint32", Size = target.IntWidth},
704 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
705 new { Name = "float", Size = target.FloatWidth},
706 new { Name = "double", Size = target.DoubleWidth},
707 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
710 // Write the size info for the basic types.
711 foreach (var size in sizes)
712 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
715 static Class GetClassFromTypedef(ITypedDecl typedef)
717 var type = typedef.Type.Desugar() as TagType;
721 var @class = type.Declaration as Class;
723 return @class.IsIncomplete ?
724 (@class.CompleteDeclaration as Class) : @class;
727 static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
728 bool optional = false)
730 foreach (var @struct in types)
732 var @class = ctx.FindCompleteClass(@struct);
734 @class = ctx.FindCompleteClass("_" + @struct);
738 var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
739 decl => !decl.IsIncomplete);
742 @class = GetClassFromTypedef(typedef);
745 if (@class == null && optional)
749 throw new Exception("Expected to find struct definition for " + @struct);
751 DumpStruct(writer, @class);
755 static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
757 var types = new List<string>
760 "MonoObjectHandlePayload",
764 "MonoInternalThread",
765 "MonoMulticastDelegate",
766 "MonoTransparentProxy",
773 "MonoComInteropProxy",
779 "SgenClientThreadInfo"
782 DumpClasses(writer, ctx, types);
785 static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
787 writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
788 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
793 "MonoMethodRuntimeGenericContext",
795 "MonoGSharedVtMethodRuntimeInfo",
798 "MonoDelegateTrampInfo",
801 DumpClasses(writer, ctx, types);
803 var optionalTypes = new[]
811 DumpClasses(writer, ctx, optionalTypes, optional: true);
813 writer.WriteLine("#endif //disable jit check");
816 static void DumpStruct(TextWriter writer, Class @class)
818 var name = @class.Name;
819 if (name.StartsWith ("_", StringComparison.Ordinal))
820 name = name.Substring (1);
822 foreach (var field in @class.Fields)
824 if (field.IsBitField) continue;
826 if (name == "SgenThreadInfo" && field.Name == "regs")
829 var layout = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr);
831 writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,