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.ASTContext, driver.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.BuildParseOptions(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.Verbose = false;
310 options.LibraryName = "Mono";
311 options.MicrosoftMode = false;
312 options.addArguments("-xc");
313 options.addArguments("-std=gnu99");
314 options.addDefines("CPPSHARP");
316 foreach (var define in target.Defines)
317 options.addDefines(define);
319 SetupToolchainPaths(driver, target);
321 SetupMono(options, target);
324 static void SetupMono(DriverOptions options, Target target)
327 switch (target.Platform) {
328 case TargetPlatform.Android:
329 targetPath = Path.Combine (MonodroidDir, XamarinAndroid ? "build-tools/mono-runtimes/obj/Debug" : "builds");
331 case TargetPlatform.WatchOS:
332 case TargetPlatform.iOS:
333 targetPath = Path.Combine (MaccoreDir, "builds");
336 throw new ArgumentOutOfRangeException ();
339 if (!Directory.Exists (MonoDir)) {
340 MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
343 var targetBuild = Path.Combine(targetPath, target.Build);
345 if (!Directory.Exists(targetBuild))
346 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
348 var includeDirs = new[]
351 Path.Combine(targetBuild, "eglib", "src"),
353 Path.Combine(MonoDir, "mono"),
354 Path.Combine(MonoDir, "mono", "mini"),
355 Path.Combine(MonoDir, "eglib", "src")
358 foreach (var inc in includeDirs)
359 options.addIncludeDirs(inc);
361 var filesToParse = new[]
363 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
364 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
367 foreach (var file in filesToParse)
368 options.Headers.Add(file);
371 static void SetupMSVC(Driver driver, string triple)
373 var options = driver.Options;
375 options.Abi = Parser.AST.CppAbi.Microsoft;
376 options.MicrosoftMode = true;
378 var systemIncludeDirs = new[]
380 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
381 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
384 foreach (var inc in systemIncludeDirs)
385 options.addSystemIncludeDirs(inc);
387 options.addDefines("HOST_WIN32");
390 static void SetupToolchainPaths(Driver driver, Target target)
392 switch (target.Platform) {
393 case TargetPlatform.Android:
394 SetupAndroidNDK(driver, target);
396 case TargetPlatform.iOS:
397 case TargetPlatform.WatchOS:
398 SetupXcode(driver, target);
401 throw new ArgumentOutOfRangeException ();
405 static string GetArchFromTriple(string triple)
407 if (triple.Contains("mips"))
410 if (triple.Contains("arm64") || triple.Contains("aarch64"))
413 if (triple.Contains("arm"))
416 if (triple.Contains("i686"))
419 if (triple.Contains("x86_64"))
422 throw new Exception("Unknown architecture from triple: " + triple);
425 static string GetXcodeToolchainPath()
427 var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
431 var toolchainPath = toolchains.LastOrDefault();
432 if (toolchainPath == null)
433 throw new Exception("Could not find a valid Xcode SDK");
435 return toolchainPath;
438 static string GetXcodeBuiltinIncludesFolder()
440 var toolchainPath = GetXcodeToolchainPath();
442 var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
443 "Contents/Developer/Toolchains")).ToList();
446 toolchainPath = toolchains.LastOrDefault();
447 if (toolchainPath == null)
448 throw new Exception("Could not find a valid Xcode toolchain");
450 var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
451 "usr/lib/clang")).ToList();
452 var includePath = includePaths.LastOrDefault();
454 if (includePath == null)
455 throw new Exception("Could not find a valid Clang include folder");
457 return Path.Combine(includePath, "include");
460 static string GetXcodeiOSIncludesFolder()
462 var toolchainPath = GetXcodeToolchainPath();
464 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
465 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
466 var sdkPath = sdkPaths.LastOrDefault();
469 throw new Exception("Could not find a valid iPhone SDK");
471 return Path.Combine(sdkPath, "usr/include");
474 static string GetXcodeWatchOSIncludesFolder()
476 var toolchainPath = GetXcodeToolchainPath();
478 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
479 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
480 var sdkPath = sdkPaths.LastOrDefault();
483 throw new Exception("Could not find a valid WatchOS SDK");
485 return Path.Combine(sdkPath, "usr/include");
488 static void SetupXcode(Driver driver, Target target)
490 var options = driver.Options;
492 var builtinsPath = GetXcodeBuiltinIncludesFolder();
495 switch (target.Platform) {
496 case TargetPlatform.iOS:
497 includePath = GetXcodeiOSIncludesFolder();
499 case TargetPlatform.WatchOS:
500 includePath = GetXcodeWatchOSIncludesFolder();
503 throw new ArgumentOutOfRangeException ();
506 options.addSystemIncludeDirs(builtinsPath);
507 options.addSystemIncludeDirs(includePath);
509 options.NoBuiltinIncludes = true;
510 options.NoStandardIncludes = true;
511 options.TargetTriple = target.Triple;
514 static string GetAndroidHostToolchainPath()
516 var androidNdkPath = GetAndroidNdkPath ();
517 var toolchains = Directory.EnumerateDirectories(
518 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
521 var toolchainPath = toolchains.LastOrDefault();
522 if (toolchainPath == null)
523 throw new Exception("Could not find a valid NDK host toolchain");
525 toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
526 "prebuilt")).ToList();
529 toolchainPath = toolchains.LastOrDefault();
530 if (toolchainPath == null)
531 throw new Exception("Could not find a valid NDK host toolchain");
533 return toolchainPath;
536 static string GetAndroidBuiltinIncludesFolder()
538 var toolchainPath = GetAndroidHostToolchainPath();
540 string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
541 if (!Directory.Exists (clangToolchainPath))
542 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
544 string includePath = null;
545 if (Directory.Exists (clangToolchainPath)) {
546 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
547 includePath = includePaths.LastOrDefault();
549 if (includePath == null)
550 throw new Exception("Could not find a valid Clang include folder");
552 return Path.Combine(includePath, "include");
555 static void SetupAndroidNDK(Driver driver, Target target)
557 var options = driver.Options;
559 var builtinsPath = GetAndroidBuiltinIncludesFolder();
560 options.addSystemIncludeDirs(builtinsPath);
562 var androidNdkRoot = GetAndroidNdkPath ();
563 const int androidNdkApiLevel = 21;
565 var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
566 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
568 options.addSystemIncludeDirs(toolchainPath);
570 options.NoBuiltinIncludes = true;
571 options.NoStandardIncludes = true;
572 options.TargetTriple = target.Triple;
575 static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
579 case ParserIntType.SignedChar:
580 case ParserIntType.UnsignedChar:
581 return target.CharAlign;
582 case ParserIntType.SignedShort:
583 case ParserIntType.UnsignedShort:
584 return target.ShortAlign;
585 case ParserIntType.SignedInt:
586 case ParserIntType.UnsignedInt:
587 return target.IntAlign;
588 case ParserIntType.SignedLong:
589 case ParserIntType.UnsignedLong:
590 return target.LongAlign;
591 case ParserIntType.SignedLongLong:
592 case ParserIntType.UnsignedLongLong:
593 return target.LongLongAlign;
595 throw new Exception("Type has no alignment");
599 static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
603 case ParserIntType.SignedChar:
604 case ParserIntType.UnsignedChar:
605 return target.CharWidth;
606 case ParserIntType.SignedShort:
607 case ParserIntType.UnsignedShort:
608 return target.ShortWidth;
609 case ParserIntType.SignedInt:
610 case ParserIntType.UnsignedInt:
611 return target.IntWidth;
612 case ParserIntType.SignedLong:
613 case ParserIntType.UnsignedLong:
614 return target.LongWidth;
615 case ParserIntType.SignedLongLong:
616 case ParserIntType.UnsignedLongLong:
617 return target.LongLongWidth;
619 throw new Exception("Type has no size");
623 static string GetTargetPlatformDefine(TargetPlatform target)
626 case TargetPlatform.Android:
627 return "TARGET_ANDROID";
628 case TargetPlatform.iOS:
630 case TargetPlatform.WatchOS:
631 return "TARGET_WATCHOS";
633 throw new ArgumentOutOfRangeException ();
637 static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
639 var targetFile = target.Triple;
641 if (!string.IsNullOrEmpty (OutputDir))
642 targetFile = Path.Combine (OutputDir, targetFile);
646 using (var writer = new StreamWriter(targetFile))
647 //using (var writer = Console.Out)
649 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
650 writer.WriteLine("#ifdef {0}", target.Defines[0]);
651 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
652 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
653 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
654 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
655 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
656 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
658 DumpAligns(writer, targetInfo);
659 DumpSizes(writer, targetInfo);
660 DumpMetadataOffsets(writer, ctx, target);
662 writer.WriteLine("#endif //disable metadata check");
664 DumpJITOffsets(writer, ctx);
666 writer.WriteLine("#endif //cross compiler checks");
667 writer.WriteLine("#endif //gc check");
668 writer.WriteLine("#endif //os check");
669 writer.WriteLine("#endif //arch check");
670 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
673 Console.WriteLine("Generated offsets file: {0}", targetFile);
676 static void DumpAligns(TextWriter writer, ParserTargetInfo target)
680 new { Name = "gint8", Align = target.CharAlign},
681 new { Name = "gint16", Align = target.ShortAlign},
682 new { Name = "gint32", Align = target.IntAlign},
683 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
684 new { Name = "float", Align = target.FloatAlign},
685 new { Name = "double", Align = target.DoubleAlign},
686 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
689 // Write the alignment info for the basic types.
690 foreach (var align in aligns)
691 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
694 static void DumpSizes(TextWriter writer, ParserTargetInfo target)
698 new { Name = "gint8", Size = target.CharWidth},
699 new { Name = "gint16", Size = target.ShortWidth},
700 new { Name = "gint32", Size = target.IntWidth},
701 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
702 new { Name = "float", Size = target.FloatWidth},
703 new { Name = "double", Size = target.DoubleWidth},
704 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
707 // Write the size info for the basic types.
708 foreach (var size in sizes)
709 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
712 static Class GetClassFromTypedef(ITypedDecl typedef)
714 var type = typedef.Type.Desugar() as TagType;
718 var @class = type.Declaration as Class;
720 return @class.IsIncomplete ?
721 (@class.CompleteDeclaration as Class) : @class;
724 static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
725 bool optional = false)
727 foreach (var @struct in types)
729 var @class = ctx.FindCompleteClass(@struct);
731 @class = ctx.FindCompleteClass("_" + @struct);
735 var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
736 decl => !decl.IsIncomplete);
739 @class = GetClassFromTypedef(typedef);
742 if (@class == null && optional)
746 throw new Exception("Expected to find struct definition for " + @struct);
748 DumpStruct(writer, @class);
752 static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
754 var types = new List<string>
760 "MonoInternalThread",
761 "MonoMulticastDelegate",
762 "MonoTransparentProxy",
769 "MonoComInteropProxy",
775 "SgenClientThreadInfo"
778 DumpClasses(writer, ctx, types);
781 static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
783 writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
784 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
789 "MonoMethodRuntimeGenericContext",
791 "MonoGSharedVtMethodRuntimeInfo",
794 "MonoDelegateTrampInfo",
797 DumpClasses(writer, ctx, types);
799 var optionalTypes = new[]
807 DumpClasses(writer, ctx, optionalTypes, optional: true);
809 writer.WriteLine("#endif //disable jit check");
812 static void DumpStruct(TextWriter writer, Class @class)
814 var name = @class.Name;
815 if (name.StartsWith ("_", StringComparison.Ordinal))
816 name = name.Substring (1);
818 foreach (var field in @class.Fields)
820 if (field.IsBitField) continue;
822 if (name == "SgenThreadInfo" && field.Name == "regs")
825 writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,