Skip to content

Commit ddb215b

Browse files
authored
Make APK and shared library alignment configurable (#9046)
Context: https://github.com/dotnet/android/wiki/Android-support-for-devices-with-16k-pages Context: #9020 Starting sometime next year Google will require all packages submitted to the Play Store to be aligned to 16 bytes, in order to support Android devices which use 16kb pages instead of the currently supported 4kb ones. The requirement also applies to native shared libaries (`.so`), which need to be linked so that their loaded sections are aligned to 16kb as well. This commit implements changes which make the alignment configurable, while still defaulting to 4kb alignment. The #9020 PR will enable (when NDK r27 is released) building of our runtime shared libraries with 16kb alignment. While strictly speaking NDK r27 is not necessary to enable 16kb alignment for the runtime (we could just modify our `CMakeLists.txt` script to pass the appropriate flag), I prefer to rely on the "official" support once NDK r27 is out. Linking of application shared libraries, however, doesn't use the NDK at all, and in this case we must pass the appropriate flag to the linker explicitly. This commit also prepares our runtime to verify alignment correctly when changing between 4k and 16k.
1 parent cbfb56d commit ddb215b

File tree

13 files changed

+68
-9
lines changed

13 files changed

+68
-9
lines changed

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@
4545

4646
<!-- Mono components -->
4747
<AndroidEnableProfiler Condition=" '$(AndroidEnableProfiler)' == ''">false</AndroidEnableProfiler>
48+
49+
<!--
50+
Android package (apt/aab) alignment, expressed as the page size in kilobytes. Two values are supported: 4 and 16.
51+
Sometime next year the default value should be changed to 16 since it's going to be a Google Play store requirement for
52+
application submissions.
53+
54+
When changing this default, change the value of `DefaultZipAlignment` in `src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs` as well
55+
-->
56+
<_AndroidZipAlignment Condition=" '$(_AndroidZipAlignment)' == '' ">4</_AndroidZipAlignment>
4857
</PropertyGroup>
4958

5059
<!-- User-facing configuration-specific defaults -->

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ namespace Xamarin.Android.Tasks
77
{
88
public class AndroidZipAlign : AndroidRunToolTask
99
{
10+
// Sometime next year the default value should be changed to 16 since it's going to be a Google Play store requirement for
11+
// application submissions
12+
internal const int DefaultZipAlignment = 4;
13+
1014
public override string TaskPrefix => "AZA";
1115

1216
[Required]
@@ -15,7 +19,7 @@ public class AndroidZipAlign : AndroidRunToolTask
1519
[Required]
1620
public ITaskItem DestinationDirectory { get; set; }
1721

18-
int alignment = 4;
22+
int alignment = DefaultZipAlignment;
1923
public int Alignment {
2024
get {return alignment;}
2125
set {alignment = value;}
@@ -53,4 +57,3 @@ protected override void LogEventsFromTextOutput (string singleLine, MessageImpor
5357
}
5458
}
5559
}
56-

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public class GeneratePackageManagerJava : AndroidTask
8080
public string AndroidSequencePointsMode { get; set; }
8181
public bool EnableSGenConcurrent { get; set; }
8282
public string? CustomBundleConfigFile { get; set; }
83+
public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment;
8384

8485
[Output]
8586
public string BuildId { get; set; }
@@ -334,6 +335,7 @@ void AddEnvironment ()
334335

335336
bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath);
336337
var jniRemappingNativeCodeInfo = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfo> (ProjectSpecificTaskObjectKey (GenerateJniRemappingNativeCode.JniRemappingNativeCodeInfoKey), RegisteredTaskObjectLifetime.Build);
338+
uint zipAlignmentMask = MonoAndroidHelper.ZipAlignmentToMask (ZipAlignmentPages);
337339
var appConfigAsmGen = new ApplicationConfigNativeAssemblyGenerator (environmentVariables, systemProperties, Log) {
338340
UsesMonoAOT = usesMonoAOT,
339341
UsesMonoLLVM = EnableLLVM,
@@ -357,6 +359,7 @@ void AddEnvironment ()
357359
JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
358360
JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount,
359361
JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
362+
ZipAlignmentMask = zipAlignmentMask,
360363
MarshalMethodsEnabled = EnableMarshalMethods,
361364
IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (),
362365
};

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ sealed class InputFiles
4444
[Required]
4545
public string AndroidBinUtilsDirectory { get; set; }
4646

47+
public int ZipAlignmentPages { get; set; } = AndroidZipAlign.DefaultZipAlignment;
48+
4749
public override System.Threading.Tasks.Task RunTaskAsync ()
4850
{
4951
return this.WhenAll (GetLinkerConfigs (), RunLinker);
@@ -129,7 +131,6 @@ IEnumerable<Config> GetLinkerConfigs ()
129131
"-soname libxamarin-app.so " +
130132
"-z relro " +
131133
"-z noexecstack " +
132-
"-z max-page-size=4096 " +
133134
"--enable-new-dtags " +
134135
"--build-id " +
135136
"--warn-shared-textrel " +
@@ -186,6 +187,10 @@ IEnumerable<Config> GetLinkerConfigs ()
186187
}
187188
}
188189

190+
uint maxPageSize = MonoAndroidHelper.ZipAlignmentToPageSize (ZipAlignmentPages);
191+
targetLinkerArgs.Add ("-z");
192+
targetLinkerArgs.Add ($"max-page-size={maxPageSize}");
193+
189194
string targetArgs = String.Join (" ", targetLinkerArgs);
190195
yield return new Config {
191196
LinkerPath = ld,

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ public sealed class ApplicationConfig
6363
public uint jnienv_registerjninatives_method_token;
6464
public uint jni_remapping_replacement_type_count;
6565
public uint jni_remapping_replacement_method_index_entry_count;
66+
public uint zip_alignment_mask;
6667
public uint mono_components_mask;
6768
public string android_package_name = String.Empty;
6869
}
6970

70-
const uint ApplicationConfigFieldCount = 26;
71+
const uint ApplicationConfigFieldCount = 27;
7172

7273
const string ApplicationConfigSymbolName = "application_config";
7374
const string AppEnvironmentVariablesSymbolName = "app_environment_variables";
@@ -326,12 +327,17 @@ static ApplicationConfig ReadApplicationConfig (EnvironmentFile envFile)
326327
ret.jni_remapping_replacement_method_index_entry_count = ConvertFieldToUInt32 ("jni_remapping_replacement_method_index_entry_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
327328
break;
328329

329-
case 24: // mono_components_mask: uint32_t / .word | .long
330+
case 24: // zip_alignment_mask: uint32_t / .word | .long
331+
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
332+
ret.zip_alignment_mask = ConvertFieldToUInt32 ("zip_alignment_mask", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
333+
break;
334+
335+
case 25: // mono_components_mask: uint32_t / .word | .long
330336
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
331337
ret.mono_components_mask = ConvertFieldToUInt32 ("mono_components_mask", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
332338
break;
333339

334-
case 25: // android_package_name: string / [pointer type]
340+
case 26: // android_package_name: string / [pointer type]
335341
Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
336342
pointers.Add (field [1].Trim ());
337343
break;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ sealed class ApplicationConfig
5555
public uint jni_remapping_replacement_type_count;
5656
public uint jni_remapping_replacement_method_index_entry_count;
5757

58+
// 3, for 4-byte alignment (4k memory pages); 15, for 16-byte alignment (16k memory pages)
59+
public uint zip_alignment_mask;
60+
5861
[NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
5962
public uint mono_components_mask;
6063
public string android_package_name = String.Empty;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ sealed class XamarinAndroidBundledAssembly
183183
public int JNIEnvRegisterJniNativesToken { get; set; }
184184
public int JniRemappingReplacementTypeCount { get; set; }
185185
public int JniRemappingReplacementMethodIndexEntryCount { get; set; }
186+
public uint ZipAlignmentMask { get; set; }
186187
public MonoComponent MonoComponents { get; set; }
187188
public PackageNamingPolicy PackageNamingPolicy { get; set; }
188189
public List<ITaskItem> NativeLibraries { get; set; }
@@ -244,6 +245,7 @@ protected override void Construct (LlvmIrModule module)
244245
jnienv_registerjninatives_method_token = (uint)JNIEnvRegisterJniNativesToken,
245246
jni_remapping_replacement_type_count = (uint)JniRemappingReplacementTypeCount,
246247
jni_remapping_replacement_method_index_entry_count = (uint)JniRemappingReplacementMethodIndexEntryCount,
248+
zip_alignment_mask = ZipAlignmentMask,
247249
mono_components_mask = (uint)MonoComponents,
248250
android_package_name = AndroidPackageName,
249251
};

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,5 +712,22 @@ internal static void DumpMarshalMethodsToConsole (string heading, IDictionary<st
712712
}
713713
}
714714
}
715+
716+
public static uint ZipAlignmentToMask (int alignment) => ZipAlignmentToMaskOrPageSize (alignment, needMask: true);
717+
public static uint ZipAlignmentToPageSize (int alignment) => ZipAlignmentToMaskOrPageSize (alignment, needMask: false);
718+
719+
static uint ZipAlignmentToMaskOrPageSize (int alignment, bool needMask)
720+
{
721+
const uint pageSize4k = 4096;
722+
const uint pageMask4k = 3;
723+
const uint pageSize16k = 16384;
724+
const uint pageMask16k = 15;
725+
726+
return alignment switch {
727+
4 => needMask ? pageMask4k : pageSize4k,
728+
16 => needMask ? pageMask16k : pageSize16k,
729+
_ => throw new InvalidOperationException ($"Internal error: unsupported zip page alignment value {alignment}")
730+
};
731+
}
715732
}
716733
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,7 @@ because xbuild doesn't support framework reference assemblies.
17241724
UseAssemblyStore="$(AndroidUseAssemblyStore)"
17251725
EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
17261726
CustomBundleConfigFile="$(AndroidBundleConfigurationFile)"
1727+
ZipAlignmentPages="$(_AndroidZipAlignment)"
17271728
>
17281729
<Output TaskParameter="BuildId" PropertyName="_XamarinBuildId" />
17291730
</GeneratePackageManagerJava>
@@ -2010,6 +2011,7 @@ because xbuild doesn't support framework reference assemblies.
20102011
ApplicationSharedLibraries="@(_ApplicationSharedLibrary)"
20112012
DebugBuild="$(AndroidIncludeDebugSymbols)"
20122013
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)"
2014+
ZipAlignmentPages="$(_AndroidZipAlignment)"
20132015
/>
20142016
<ItemGroup>
20152017
<FileWrites Include="@(_ApplicationSharedLibrary)" />
@@ -2354,6 +2356,7 @@ because xbuild doesn't support framework reference assemblies.
23542356
<Delete Files="%(ApkAbiFilesSigned.FullPath)" Condition=" '$(AndroidUseApkSigner)' == 'true' "/>
23552357
<AndroidZipAlign Condition=" '$(AndroidUseApkSigner)' == 'true' "
23562358
Source="%(ApkAbiFilesIntermediate.Identity)"
2359+
Alignment="$(_AndroidZipAlignment)"
23572360
DestinationDirectory="$(OutDir)"
23582361
ToolPath="$(ZipAlignToolPath)"
23592362
ToolExe="$(ZipalignToolExe)"
@@ -2389,6 +2392,7 @@ because xbuild doesn't support framework reference assemblies.
23892392
<Message Text="Unaligned android package '%(ApkAbiFilesUnaligned.FullPath)'" Condition=" '$(AndroidUseApkSigner)' != 'True' And '$(AndroidPackageFormat)' != 'aab' "/>
23902393
<AndroidZipAlign Condition=" '$(AndroidUseApkSigner)' != 'True' And '$(AndroidPackageFormat)' != 'aab' "
23912394
Source="%(ApkAbiFilesUnaligned.Identity)"
2395+
Alignment="$(_AndroidZipAlignment)"
23922396
DestinationDirectory="$(OutDir)"
23932397
ToolPath="$(ZipAlignToolPath)"
23942398
ToolExe="$(ZipalignToolExe)"

src/native/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ set(POTENTIAL_XA_COMMON_COMPILER_ARGS
374374
-Wformat-security
375375
-Wformat=2
376376
-Wno-format-nonliteral
377+
-Wno-vla-cxx-extension
377378
-Wimplicit-fallthrough
378379
-Wmisleading-indentation
379380
-Wnull-dereference

0 commit comments

Comments
 (0)