|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.IO; |
| 4 | +using System.Text; |
| 5 | + |
| 6 | +using Microsoft.Build.Framework; |
| 7 | +using Microsoft.Build.Utilities; |
| 8 | +using Xamarin.Android.Tools; |
| 9 | + |
| 10 | +namespace Xamarin.Android.Tasks; |
| 11 | + |
| 12 | +class ELFEmbeddingHelper |
| 13 | +{ |
| 14 | + public sealed class EmbedItem |
| 15 | + { |
| 16 | + public readonly string SymbolName; |
| 17 | + public readonly string BaseFileName; |
| 18 | + |
| 19 | + public EmbedItem (string symbolName, string baseFileName) |
| 20 | + { |
| 21 | + SymbolName = symbolName; |
| 22 | + BaseFileName = baseFileName; |
| 23 | + } |
| 24 | + } |
| 25 | + |
| 26 | + public static class KnownEmbedItems |
| 27 | + { |
| 28 | + public static readonly EmbedItem RuntimeConfig = new ("embedded_runtime_config", "runtime_config"); |
| 29 | + } |
| 30 | + |
| 31 | + sealed class LlvmMcTargetConfig |
| 32 | + { |
| 33 | + public readonly string TargetArch; |
| 34 | + public readonly string TripleArch; |
| 35 | + public readonly string TripleApiPrefix; |
| 36 | + public readonly string AssemblerDirectivePrefix; |
| 37 | + public readonly string SizeType; |
| 38 | + public readonly uint WordSize; |
| 39 | + |
| 40 | + public LlvmMcTargetConfig (string targetArch, string tripleArch, string tripleApiPrefix, string assemblerDirectivePrefix, string sizeType, uint wordSize) |
| 41 | + { |
| 42 | + TargetArch = targetArch; |
| 43 | + TripleArch = tripleArch; |
| 44 | + TripleApiPrefix = tripleApiPrefix; |
| 45 | + AssemblerDirectivePrefix = assemblerDirectivePrefix; |
| 46 | + SizeType = sizeType; |
| 47 | + WordSize = wordSize; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + static readonly Dictionary<AndroidTargetArch, LlvmMcTargetConfig> llvmMcConfigs = new () { |
| 52 | + { AndroidTargetArch.Arm64, new ("aarch64", "aarch64", "android", "@", ".xword", 8) }, |
| 53 | + { AndroidTargetArch.Arm, new ("arm", "armv7a", "androideabi", "%", ".long", 4) }, |
| 54 | + { AndroidTargetArch.X86_64, new ("x86-64", "x86_64", "android", "@", ".quad", 8) }, |
| 55 | + { AndroidTargetArch.X86, new ("x86", "i686", "android", "@", ".long", 4) }, |
| 56 | + }; |
| 57 | + |
| 58 | + static readonly Encoding asmFileEncoding = new UTF8Encoding (false); |
| 59 | + |
| 60 | + public static List<ITaskItem> EmbedBinary ( |
| 61 | + TaskLoggingHelper log, |
| 62 | + ICollection<string> supportedAbis, |
| 63 | + string androidBinUtilsDirectory, |
| 64 | + string inputFile, |
| 65 | + EmbedItem embedItem, |
| 66 | + string outputDirectory) |
| 67 | + { |
| 68 | + if (supportedAbis.Count < 1) { |
| 69 | + throw new ArgumentException ("At least one target ABI must be present", nameof (supportedAbis)); |
| 70 | + } |
| 71 | + |
| 72 | + string llvmMcPath = GetLlvmMcPath (androidBinUtilsDirectory); |
| 73 | + var ret = new List<ITaskItem> (); |
| 74 | + foreach (string abi in supportedAbis) { |
| 75 | + EmbedBinary ( |
| 76 | + log, |
| 77 | + ret, |
| 78 | + llvmMcPath, |
| 79 | + abi, |
| 80 | + inputFile, |
| 81 | + outputDirectory, |
| 82 | + embedItem |
| 83 | + ); |
| 84 | + } |
| 85 | + |
| 86 | + return ret; |
| 87 | + } |
| 88 | + |
| 89 | + public static List<ITaskItem> EmbedBinary ( |
| 90 | + TaskLoggingHelper log, |
| 91 | + string abi, |
| 92 | + string androidBinUtilsDirectory, |
| 93 | + string inputFile, |
| 94 | + EmbedItem embedItem, |
| 95 | + string outputDirectory) |
| 96 | + { |
| 97 | + if (String.IsNullOrEmpty (abi)) { |
| 98 | + throw new ArgumentException ("Must be a supported ABI name", nameof (abi)); |
| 99 | + } |
| 100 | + |
| 101 | + var ret = new List<ITaskItem> (); |
| 102 | + EmbedBinary ( |
| 103 | + log, |
| 104 | + ret, |
| 105 | + GetLlvmMcPath (androidBinUtilsDirectory), |
| 106 | + abi, |
| 107 | + inputFile, |
| 108 | + outputDirectory, |
| 109 | + embedItem |
| 110 | + ); |
| 111 | + return ret; |
| 112 | + } |
| 113 | + |
| 114 | + static void EmbedBinary ( |
| 115 | + TaskLoggingHelper log, |
| 116 | + List<ITaskItem> resultItems, |
| 117 | + string llvmMcPath, |
| 118 | + string abi, |
| 119 | + string inputFile, |
| 120 | + string outputDirectory, |
| 121 | + EmbedItem embedItem) |
| 122 | + { |
| 123 | + string outputFile = Path.Combine (outputDirectory, $"embed_{embedItem.BaseFileName}.{abi.ToLowerInvariant ()}.o"); |
| 124 | + DoEmbed (log, MonoAndroidHelper.AbiToTargetArch (abi), llvmMcPath, inputFile, outputFile, embedItem); |
| 125 | + if (!File.Exists (outputFile)) { |
| 126 | + return; |
| 127 | + } |
| 128 | + |
| 129 | + var taskItem = new TaskItem (outputFile); |
| 130 | + taskItem.SetMetadata ("Abi", abi); |
| 131 | + taskItem.SetMetadata ("RuntimeIdentifier", MonoAndroidHelper.AbiToRid (abi)); |
| 132 | + resultItems.Add (taskItem); |
| 133 | + } |
| 134 | + |
| 135 | + static void DoEmbed ( |
| 136 | + TaskLoggingHelper log, |
| 137 | + AndroidTargetArch arch, |
| 138 | + string llvmMcPath, |
| 139 | + string inputFile, |
| 140 | + string outputFile, |
| 141 | + EmbedItem item) |
| 142 | + { |
| 143 | + if (!llvmMcConfigs.TryGetValue (arch, out LlvmMcTargetConfig cfg)) { |
| 144 | + throw new NotSupportedException ($"Internal error: unsupported target arch '{arch}'"); |
| 145 | + } |
| 146 | + |
| 147 | + var fi = new FileInfo (inputFile); |
| 148 | + long inputFileSize = fi.Length; |
| 149 | + string asmInputFile = Path.ChangeExtension (outputFile, ".s"); |
| 150 | + |
| 151 | + using var fs = File.Open (asmInputFile, FileMode.Create, FileAccess.Write, FileShare.Read); |
| 152 | + using var sw = new StreamWriter (fs, asmFileEncoding); |
| 153 | + |
| 154 | + string symbolName = item.SymbolName; |
| 155 | + sw.WriteLine ($".section .rodata,\"a\",{cfg.AssemblerDirectivePrefix}progbits"); |
| 156 | + sw.WriteLine (".p2align 3, 0x00"); // Put the data at 4k boundary |
| 157 | + sw.WriteLine (); |
| 158 | + sw.WriteLine ($".global {symbolName}"); |
| 159 | + sw.WriteLine ($".type {symbolName},{cfg.AssemblerDirectivePrefix}object"); |
| 160 | + sw.WriteLine ($"{symbolName}:"); |
| 161 | + sw.WriteLine ($"\t.incbin \"{inputFile}\""); |
| 162 | + sw.WriteLine ($"\t.size {symbolName}, {inputFileSize}"); |
| 163 | + sw.WriteLine (); |
| 164 | + |
| 165 | + symbolName += "_size"; |
| 166 | + sw.WriteLine ($".global {symbolName}"); |
| 167 | + sw.WriteLine ($"{symbolName}:"); |
| 168 | + sw.WriteLine ($"\t{cfg.SizeType}\t{inputFileSize}"); |
| 169 | + sw.WriteLine ($"\t.size {symbolName}, {cfg.WordSize}"); |
| 170 | + |
| 171 | + sw.Flush (); |
| 172 | + sw.Close (); |
| 173 | + |
| 174 | + var args = new List<string> { |
| 175 | + $"--arch={cfg.TargetArch}", |
| 176 | + "--assemble", |
| 177 | + "--filetype=obj", |
| 178 | + "-g", |
| 179 | + $"--triple={cfg.TripleArch}-linux-{cfg.TripleApiPrefix}{XABuildConfig.AndroidMinimumDotNetApiLevel}", |
| 180 | + "-o", MonoAndroidHelper.QuoteFileNameArgument (outputFile), |
| 181 | + MonoAndroidHelper.QuoteFileNameArgument (asmInputFile), |
| 182 | + }; |
| 183 | + |
| 184 | + int ret = MonoAndroidHelper.RunProcess (llvmMcPath, String.Join (" ", args), log); |
| 185 | + if (ret != 0) { |
| 186 | + return; |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + static string GetLlvmMcPath (string androidBinUtilsDirectory) => MonoAndroidHelper.GetLlvmMcPath (androidBinUtilsDirectory); |
| 191 | +} |
0 commit comments