Skip to content

Commit 1c14cea

Browse files
committed
Use llvm-mc to embed the binary, this way we have full control
1 parent be7efec commit 1c14cea

File tree

7 files changed

+216
-2
lines changed

7 files changed

+216
-2
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public class GeneratePackageManagerJava : AndroidTask
6464
[Required]
6565
public string AndroidBinUtilsDirectory { get; set; }
6666

67+
[Output]
68+
public ITaskItem[] EmbeddedObjectFiles { get; set; }
69+
6770
public bool EnableMarshalMethods { get; set; }
6871
public string RuntimeConfigBinFilePath { get; set; }
6972
public string BoundExceptionType { get; set; }
@@ -332,6 +335,10 @@ void AddEnvironment ()
332335
ELFEmbeddingHelper.KnownEmbedItems.RuntimeConfig,
333336
EnvironmentOutputDirectory
334337
);
338+
339+
EmbeddedObjectFiles = objectFilePaths.ToArray ();
340+
} else {
341+
EmbeddedObjectFiles = Array.Empty<ITaskItem> ();
335342
}
336343

337344
var jniRemappingNativeCodeInfo = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfo> (ProjectSpecificTaskObjectKey (GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfoKey), RegisteredTaskObjectLifetime.Build);
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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+
}

src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -789,9 +789,12 @@ public static string QuoteFileNameArgument (string fileName)
789789
return builder.ToString ();
790790
}
791791

792-
public static string GetLlvmObjcopyPath (string androidBinUtilsDirectory)
792+
public static string GetLlvmObjcopyPath (string androidBinUtilsDirectory) => GetBinUtilsToolPath (androidBinUtilsDirectory, "llvm-objcopy");
793+
public static string GetLlvmMcPath (string androidBinUtilsDirectory) => GetBinUtilsToolPath (androidBinUtilsDirectory, "llvm-mc");
794+
795+
static string GetBinUtilsToolPath (string androidBinUtilsDirectory, string toolName)
793796
{
794-
return Path.Combine (androidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (androidBinUtilsDirectory, "llvm-objcopy"));
797+
return Path.Combine (androidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (androidBinUtilsDirectory, toolName));
795798
}
796799
}
797800
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,7 @@ because xbuild doesn't support framework reference assemblies.
17091709
<Target Name="_GetGeneratePackageManagerJavaInputs">
17101710
<ItemGroup>
17111711
<_GeneratePackageManagerJavaInputs Include="@(_GenerateJavaStubsInputs)" />
1712+
<_GeneratePackageManagerJavaInputs Include="$(_BinaryRuntimeConfigPath)" />
17121713
</ItemGroup>
17131714
</Target>
17141715

@@ -1748,10 +1749,12 @@ because xbuild doesn't support framework reference assemblies.
17481749
CustomBundleConfigFile="$(AndroidBundleConfigurationFile)"
17491750
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
17501751
>
1752+
<Output TaskParameter="EmbeddedObjectFiles" ItemName="_NativeAssemblyTarget" />
17511753
</GeneratePackageManagerJava>
17521754
<Touch Files="$(_AndroidStampDirectory)_GeneratePackageManagerJava.stamp" AlwaysCreate="True" />
17531755
<ItemGroup>
17541756
<FileWrites Include="@(_EnvironmentAssemblySource)" />
1757+
<FileWrites Include="@(_NativeAssemblyTarget)" />
17551758
</ItemGroup>
17561759
</Target>
17571760

src/native/monodroid/monodroid-glue.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,11 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl
13811381

13821382
Logger::init_logging_categories (mono_log_mask_raw, mono_log_level_raw);
13831383

1384+
log_warn (LOG_DEFAULT, "Embedded runtime config size: %zu", embedded_runtime_config_size);
1385+
if (embedded_runtime_config_size > 0) {
1386+
log_warn (LOG_DEFAULT, "First byte of embedded runtime config: 0x%x", embedded_runtime_config[0]);
1387+
}
1388+
13841389
std::unique_ptr<char[]> mono_log_mask (mono_log_mask_raw);
13851390
std::unique_ptr<char[]> mono_log_level (mono_log_level_raw);
13861391

src/native/xamarin-app-stub/application_dso_stub.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,6 @@ const JniRemappingTypeReplacementEntry jni_remapping_type_replacements[] = {
303303
.replacement = "another/replacement/java/type",
304304
},
305305
};
306+
307+
size_t embedded_runtime_config_size = 0;
308+
uint8_t embedded_runtime_config[0];

src/native/xamarin-app-stub/xamarin-app.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,6 @@ using get_function_pointer_fn = void(*)(uint32_t mono_image_index, uint32_t clas
396396
MONO_API MONO_API_EXPORT void xamarin_app_init (JNIEnv *env, get_function_pointer_fn fn) noexcept;
397397
#endif // def RELEASE
398398

399+
MONO_API MONO_API_EXPORT size_t embedded_runtime_config_size;
400+
MONO_API MONO_API_EXPORT uint8_t embedded_runtime_config[];
399401
#endif // __XAMARIN_ANDROID_TYPEMAP_H

0 commit comments

Comments
 (0)