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 AndroidNdkPath = @"";
29 static string MaccoreDir = @"";
30 static string TargetDir = @"";
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 Defines = { "TARGET_X86" }
95 Targets.Add (new Target {
96 Platform = TargetPlatform.Android,
97 Triple = "x86_64-none-linux-android",
98 Defines = { "TARGET_AMD64" }
101 Targets.Add (new Target {
102 Platform = TargetPlatform.Android,
103 Triple = "armv5-none-linux-androideabi",
104 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
107 Targets.Add (new Target {
108 Platform = TargetPlatform.Android,
109 Triple = "armv7-none-linux-androideabi",
110 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5", "HAVE_ARMV6",
115 Targets.Add (new Target {
116 Platform = TargetPlatform.Android,
117 Triple = "aarch64-v8a-linux-android",
118 Defines = { "TARGET_ARM64" }
121 /*Targets.Add(new Target {
122 Platform = TargetPlatform.Android,
123 Triple = "mipsel-none-linux-android",
124 Defines = { "TARGET_MIPS", "__mips__" }
127 foreach (var target in AndroidTargets)
128 target.Defines.AddRange (new string[] { "PLATFORM_ANDROID",
129 "TARGET_ANDROID", "MONO_CROSS_COMPILE", "USE_MONO_CTX"
133 public static void SetupiOSTargets()
135 Targets.Add(new Target {
136 Platform = TargetPlatform.iOS,
137 Triple = "arm-apple-darwin10",
139 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
142 Targets.Add(new Target {
143 Platform = TargetPlatform.iOS,
144 Triple = "aarch64-apple-darwin10",
146 Defines = { "TARGET_ARM64" }
149 foreach (var target in iOSTargets) {
150 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
151 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
156 Targets.Add(new Target {
157 Platform = TargetPlatform.WatchOS,
158 Triple = "armv7k-apple-darwin",
159 Build = "targetwatch",
160 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
163 foreach (var target in DarwinTargets) {
164 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
165 "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
171 static bool GetParentSubDirectoryPath(string parent, out string subdir)
173 var directory = Directory.GetParent(Directory.GetCurrentDirectory());
175 while (directory != null) {
176 var path = Path.Combine(directory.FullName, parent);
178 if (Directory.Exists (path)) {
183 directory = directory.Parent;
190 public static void Main(string[] args)
192 ParseCommandLineArgs(args);
195 if (!Directory.Exists (MonodroidDir) &&
196 GetParentSubDirectoryPath ("monodroid", out monodroidDir)) {
197 MonodroidDir = Path.Combine (monodroidDir);
200 if (Directory.Exists (MonodroidDir))
201 SetupAndroidTargets();
204 if (!Directory.Exists (MaccoreDir) &&
205 GetParentSubDirectoryPath ("maccore", out maccoreDir)) {
206 MaccoreDir = Path.Combine (maccoreDir);
209 if (Directory.Exists(MaccoreDir))
212 foreach (var target in Targets)
214 if (Abis.Any() && !Abis.Any (target.Triple.Contains))
218 Console.WriteLine("Processing triple: {0}", target.Triple);
220 var options = new DriverOptions();
222 var driver = new Driver(options);
224 Setup(driver, target);
227 BuildParseOptions(driver, target);
228 if (!driver.ParseCode())
231 Dump(driver.Context.ASTContext, driver.Context.TargetInfo, target);
235 static void BuildParseOptions(Driver driver, Target target)
237 foreach (var header in driver.Options.Headers)
239 var source = driver.Project.AddFile(header);
240 source.Options = driver.BuildParserOptions(source);
242 if (header.Contains ("mini"))
245 source.Options.AddDefines ("HAVE_SGEN_GC");
246 source.Options.AddDefines ("HAVE_MOVING_COLLECTOR");
250 static string GetAndroidNdkPath()
252 if (!String.IsNullOrEmpty (AndroidNdkPath))
253 return AndroidNdkPath;
255 // Find the Android NDK's path from Monodroid's config.
256 var configFile = Path.Combine(MonodroidDir, "env.config");
257 if (!File.Exists(configFile))
258 throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
260 var config = File.ReadAllText(configFile);
261 var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
262 return match.Groups[1].Value.Trim();
265 static void ParseCommandLineArgs(string[] args)
267 var showHelp = false;
269 var options = new Mono.Options.OptionSet () {
270 { "abi=", "ABI triple to generate", v => Abis.Add(v) },
271 { "o|out=", "output directory", v => OutputDir = v },
272 { "maccore=", "include directory", v => MaccoreDir = v },
273 { "monodroid=", "top monodroid directory", v => MonodroidDir = v },
274 { "android-ndk=", "Path to Android NDK", v => AndroidNdkPath = v },
275 { "targetdir=", "Path to the directory containing the mono build", v =>TargetDir = 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} <options>",
292 AppDomain.CurrentDomain.FriendlyName);
293 options.WriteOptionDescriptions (Console.Out);
298 static void Setup(Driver driver, Target target)
300 var options = driver.Options;
301 options.DryRun = true;
302 options.LibraryName = "Mono";
304 var parserOptions = driver.ParserOptions;
305 parserOptions.Verbose = false;
306 parserOptions.MicrosoftMode = false;
307 parserOptions.AddArguments("-xc");
308 parserOptions.AddArguments("-std=gnu99");
309 parserOptions.AddDefines("CPPSHARP");
311 foreach (var define in target.Defines)
312 parserOptions.AddDefines(define);
314 SetupToolchainPaths(driver, target);
316 SetupMono(driver, target);
319 static void SetupMono(Driver driver, Target target)
322 switch (target.Platform) {
323 case TargetPlatform.Android:
324 if (TargetDir == "") {
325 Console.Error.WriteLine ("The --targetdir= option is required when targeting android.");
326 Environment.Exit (1);
329 Console.Error.WriteLine ("The --mono= option is required when targeting android.");
330 Environment.Exit (1);
332 if (Abis.Count != 1) {
333 Console.Error.WriteLine ("Exactly one --abi= argument is required when targeting android.");
334 Environment.Exit (1);
336 targetBuild = TargetDir;
338 case TargetPlatform.WatchOS:
339 case TargetPlatform.iOS: {
340 string targetPath = Path.Combine (MaccoreDir, "builds");
341 if (!Directory.Exists (MonoDir))
342 MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
343 targetBuild = Path.Combine(targetPath, target.Build);
347 throw new ArgumentOutOfRangeException ();
350 if (!Directory.Exists(targetBuild))
351 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
353 var includeDirs = new[]
356 Path.Combine(targetBuild, "eglib", "src"),
358 Path.Combine(MonoDir, "mono"),
359 Path.Combine(MonoDir, "mono", "mini"),
360 Path.Combine(MonoDir, "eglib", "src")
363 foreach (var inc in includeDirs)
364 driver.ParserOptions.AddIncludeDirs(inc);
366 var filesToParse = new[]
368 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
369 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
372 foreach (var file in filesToParse)
373 driver.Options.Headers.Add(file);
376 static void SetupMSVC(Driver driver, string triple)
378 var parserOptions = driver.ParserOptions;
380 parserOptions.Abi = Parser.AST.CppAbi.Microsoft;
381 parserOptions.MicrosoftMode = true;
383 var systemIncludeDirs = new[]
385 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
386 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
389 foreach (var inc in systemIncludeDirs)
390 parserOptions.AddSystemIncludeDirs(inc);
392 parserOptions.AddDefines("HOST_WIN32");
395 static void SetupToolchainPaths(Driver driver, Target target)
397 switch (target.Platform) {
398 case TargetPlatform.Android:
399 SetupAndroidNDK(driver, target);
401 case TargetPlatform.iOS:
402 case TargetPlatform.WatchOS:
403 SetupXcode(driver, target);
406 throw new ArgumentOutOfRangeException ();
410 static string GetArchFromTriple(string triple)
412 if (triple.Contains("mips"))
415 if (triple.Contains("arm64") || triple.Contains("aarch64"))
418 if (triple.Contains("arm"))
421 if (triple.Contains("i686"))
424 if (triple.Contains("x86_64"))
427 throw new Exception("Unknown architecture from triple: " + triple);
430 static string GetXcodeToolchainPath()
432 var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
436 var toolchainPath = toolchains.LastOrDefault();
437 if (toolchainPath == null)
438 throw new Exception("Could not find a valid Xcode SDK");
440 return toolchainPath;
443 static string GetXcodeBuiltinIncludesFolder()
445 var toolchainPath = GetXcodeToolchainPath();
447 var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
448 "Contents/Developer/Toolchains")).ToList();
451 toolchainPath = toolchains.LastOrDefault();
452 if (toolchainPath == null)
453 throw new Exception("Could not find a valid Xcode toolchain");
455 var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
456 "usr/lib/clang")).ToList();
457 var includePath = includePaths.LastOrDefault();
459 if (includePath == null)
460 throw new Exception("Could not find a valid Clang include folder");
462 return Path.Combine(includePath, "include");
465 static string GetXcodeiOSIncludesFolder()
467 var toolchainPath = GetXcodeToolchainPath();
469 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
470 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
471 var sdkPath = sdkPaths.LastOrDefault();
474 throw new Exception("Could not find a valid iPhone SDK");
476 return Path.Combine(sdkPath, "usr/include");
479 static string GetXcodeWatchOSIncludesFolder()
481 var toolchainPath = GetXcodeToolchainPath();
483 var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
484 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
485 var sdkPath = sdkPaths.LastOrDefault();
488 throw new Exception("Could not find a valid WatchOS SDK");
490 return Path.Combine(sdkPath, "usr/include");
493 static void SetupXcode(Driver driver, Target target)
495 var parserOptions = driver.ParserOptions;
497 var builtinsPath = GetXcodeBuiltinIncludesFolder();
500 switch (target.Platform) {
501 case TargetPlatform.iOS:
502 includePath = GetXcodeiOSIncludesFolder();
504 case TargetPlatform.WatchOS:
505 includePath = GetXcodeWatchOSIncludesFolder();
508 throw new ArgumentOutOfRangeException ();
511 parserOptions.AddSystemIncludeDirs(builtinsPath);
512 parserOptions.AddSystemIncludeDirs(includePath);
514 parserOptions.NoBuiltinIncludes = true;
515 parserOptions.NoStandardIncludes = true;
516 parserOptions.TargetTriple = target.Triple;
519 static string GetAndroidHostToolchainPath()
521 var androidNdkPath = GetAndroidNdkPath ();
522 var toolchains = Directory.EnumerateDirectories(
523 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
526 var toolchainPath = toolchains.LastOrDefault();
527 if (toolchainPath == null)
528 throw new Exception("Could not find a valid NDK host toolchain");
530 toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
531 "prebuilt")).ToList();
534 toolchainPath = toolchains.LastOrDefault();
535 if (toolchainPath == null)
536 throw new Exception("Could not find a valid NDK host toolchain");
538 return toolchainPath;
541 static string GetAndroidBuiltinIncludesFolder()
543 var toolchainPath = GetAndroidHostToolchainPath();
545 string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
546 if (!Directory.Exists (clangToolchainPath))
547 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
549 string includePath = null;
550 if (Directory.Exists (clangToolchainPath)) {
551 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
552 includePath = includePaths.LastOrDefault();
554 if (includePath == null)
555 throw new Exception("Could not find a valid Clang include folder");
557 return Path.Combine(includePath, "include");
560 static void SetupAndroidNDK(Driver driver, Target target)
562 var options = driver.Options;
563 var parserOptions = driver.ParserOptions;
565 var builtinsPath = GetAndroidBuiltinIncludesFolder();
566 parserOptions.AddSystemIncludeDirs(builtinsPath);
568 var androidNdkRoot = GetAndroidNdkPath ();
569 const int androidNdkApiLevel = 21;
571 var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
572 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
574 parserOptions.AddSystemIncludeDirs(toolchainPath);
576 parserOptions.NoBuiltinIncludes = true;
577 parserOptions.NoStandardIncludes = true;
578 parserOptions.TargetTriple = target.Triple;
581 static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
585 case ParserIntType.SignedChar:
586 case ParserIntType.UnsignedChar:
587 return target.CharAlign;
588 case ParserIntType.SignedShort:
589 case ParserIntType.UnsignedShort:
590 return target.ShortAlign;
591 case ParserIntType.SignedInt:
592 case ParserIntType.UnsignedInt:
593 return target.IntAlign;
594 case ParserIntType.SignedLong:
595 case ParserIntType.UnsignedLong:
596 return target.LongAlign;
597 case ParserIntType.SignedLongLong:
598 case ParserIntType.UnsignedLongLong:
599 return target.LongLongAlign;
601 throw new Exception("Type has no alignment");
605 static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
609 case ParserIntType.SignedChar:
610 case ParserIntType.UnsignedChar:
611 return target.CharWidth;
612 case ParserIntType.SignedShort:
613 case ParserIntType.UnsignedShort:
614 return target.ShortWidth;
615 case ParserIntType.SignedInt:
616 case ParserIntType.UnsignedInt:
617 return target.IntWidth;
618 case ParserIntType.SignedLong:
619 case ParserIntType.UnsignedLong:
620 return target.LongWidth;
621 case ParserIntType.SignedLongLong:
622 case ParserIntType.UnsignedLongLong:
623 return target.LongLongWidth;
625 throw new Exception("Type has no size");
629 static string GetTargetPlatformDefine(TargetPlatform target)
632 case TargetPlatform.Android:
633 return "TARGET_ANDROID";
634 case TargetPlatform.iOS:
636 case TargetPlatform.WatchOS:
637 return "TARGET_WATCHOS";
639 throw new ArgumentOutOfRangeException ();
643 static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
645 var targetFile = target.Triple;
647 if (!string.IsNullOrEmpty (OutputDir))
648 targetFile = Path.Combine (OutputDir, targetFile);
652 using (var writer = new StreamWriter(targetFile))
653 //using (var writer = Console.Out)
655 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
656 writer.WriteLine("#ifdef {0}", target.Defines[0]);
657 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
658 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
659 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
660 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
661 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
662 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
664 DumpAligns(writer, targetInfo);
665 DumpSizes(writer, targetInfo);
666 DumpMetadataOffsets(writer, ctx, target);
668 writer.WriteLine("#endif //disable metadata check");
670 DumpJITOffsets(writer, ctx);
672 writer.WriteLine("#endif //cross compiler checks");
673 writer.WriteLine("#endif //gc check");
674 writer.WriteLine("#endif //os check");
675 writer.WriteLine("#endif //arch check");
676 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
679 Console.WriteLine("Generated offsets file: {0}", targetFile);
682 static void DumpAligns(TextWriter writer, ParserTargetInfo target)
686 new { Name = "gint8", Align = target.CharAlign},
687 new { Name = "gint16", Align = target.ShortAlign},
688 new { Name = "gint32", Align = target.IntAlign},
689 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
690 new { Name = "float", Align = target.FloatAlign},
691 new { Name = "double", Align = target.DoubleAlign},
692 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
695 // Write the alignment info for the basic types.
696 foreach (var align in aligns)
697 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
700 static void DumpSizes(TextWriter writer, ParserTargetInfo target)
704 new { Name = "gint8", Size = target.CharWidth},
705 new { Name = "gint16", Size = target.ShortWidth},
706 new { Name = "gint32", Size = target.IntWidth},
707 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
708 new { Name = "float", Size = target.FloatWidth},
709 new { Name = "double", Size = target.DoubleWidth},
710 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
713 // Write the size info for the basic types.
714 foreach (var size in sizes)
715 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
718 static Class GetClassFromTypedef(ITypedDecl typedef)
720 var type = typedef.Type.Desugar() as TagType;
724 var @class = type.Declaration as Class;
726 return @class.IsIncomplete ?
727 (@class.CompleteDeclaration as Class) : @class;
730 static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
731 bool optional = false)
733 foreach (var @struct in types)
735 var @class = ctx.FindCompleteClass(@struct);
737 @class = ctx.FindCompleteClass("_" + @struct);
741 var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
742 decl => !decl.IsIncomplete);
745 @class = GetClassFromTypedef(typedef);
748 if (@class == null && optional)
752 throw new Exception("Expected to find struct definition for " + @struct);
754 DumpStruct(writer, @class);
758 static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
760 var types = new List<string>
763 "MonoObjectHandlePayload",
767 "MonoInternalThread",
768 "MonoMulticastDelegate",
769 "MonoTransparentProxy",
776 "MonoComInteropProxy",
782 "SgenClientThreadInfo"
785 DumpClasses(writer, ctx, types);
788 static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
790 writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
791 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
796 "MonoMethodRuntimeGenericContext",
798 "MonoGSharedVtMethodRuntimeInfo",
801 "MonoDelegateTrampInfo",
804 DumpClasses(writer, ctx, types);
806 var optionalTypes = new[]
814 DumpClasses(writer, ctx, optionalTypes, optional: true);
816 writer.WriteLine("#endif //disable jit check");
819 static void DumpStruct(TextWriter writer, Class @class)
821 var name = @class.Name;
822 if (name.StartsWith ("_", StringComparison.Ordinal))
823 name = name.Substring (1);
825 foreach (var field in @class.Fields)
827 if (field.IsBitField) continue;
829 if (name == "SgenThreadInfo" && field.Name == "regs")
832 var layout = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr);
834 writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,