Skip to content

Commit

Permalink
Support DSOs embedded in the .apk (dotnet#2154)
Browse files Browse the repository at this point in the history
Context: dotnet#1906

Android v6.0 (API-23) introduced a new way of dealing with the native
shared libraries shipped in the `.apk`.  Before API-23, the libraries
would be always extracted and placed in the application data
directory, thus occupying more space than necessary.  API-23 added a
new `AndroidManifest.xml` attribute,
`//application/@android:extractNativeLibs`, which if set to `false`
makes Android *not* extract the libraries to the filesystem.  API-23
added a way to instead load those libraries directly from the `.apk`.
In order to support that there are a few requirements which this
commit implements:

  * DSO (`.so`) files must be *stored uncompressed* in the `.apk`.
  * `<application android:extractNativeLibs="false"/>` must be set
  * DSOs in the `.apk` must be aligned on the memory page boundary;
    `zipalign -p` takes care of this.

This commit also implements `libmonodroid.so` suport for loading our
DSOs directly from the `.apk`.  This operation mode is enabled by the
presence of the `$__XA_DSO_IN_APK` environment variable.  This
environment variable is inserted into the application's environment
by way of placing it in the environment file (a file part of the
Xamarin.Android App project that has the `@(AndroidEnvironment)`
build action).  In this mode, the DSOs are *no longer* looked up in
the application data directory but only in the override directories
(if the APK is built in Debug configuration) and the `.apk` itself.

Currently, in order to activate the above mode, one has to perform
the following actions manually:

 1. Add the `android:extractNativeLibs="false"` attribute to the
    `<application>/` element in `Properties/AndroidManifest.xml`.

 2. Add the following property to the project file:

        <AndroidStoreUncompressedFileExtensions>.so</AndroidStoreUncompressedFileExtensions>

 3. Add an android environment file to the project with a line which says

      __XA_DSO_IN_APK=1

After that the application should work in the embedded DSO mode
without problems.

A couple of tests are provided to test building and execution of
embedded DSO application on device, as well as to validate the
built `.apk`.

TODO: fix issue dotnet#1906 by "nicely integrating" this support with the
Xamarin.Android build system so that the above manual steps are not
required.  The exact semantics still need to be determined.
  • Loading branch information
grendello authored and jonpryor committed Sep 17, 2018
1 parent d8f1783 commit 95ca102
Show file tree
Hide file tree
Showing 32 changed files with 1,151 additions and 184 deletions.
12 changes: 12 additions & 0 deletions Xamarin.Android-Tests.sln
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Performance-Tests", "Perfor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "timing", "build-tools\timing\timing.csproj", "{37CAA28C-40BE-4253-BA68-CC5D7316A617}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedDSO", "tests\EmbeddedDSOs\EmbeddedDSO\EmbeddedDSO.csproj", "{056ED976-618F-4A3E-910E-AA25230C2296}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedDSO-UnitTests", "tests\EmbeddedDSOs\EmbeddedDSO-UnitTests\EmbeddedDSO-UnitTests.csproj", "{B160F0E7-799A-4EB9-92B8-D71623C7674A}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
tests\Xamarin.Forms-Performance-Integration\Xamarin.Forms.Performance.Integration.projitems*{195be9c2-1f91-40dc-bd6d-de860bf083fb}*SharedItemsImports = 13
Expand Down Expand Up @@ -194,6 +198,14 @@ Global
{37CAA28C-40BE-4253-BA68-CC5D7316A617}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37CAA28C-40BE-4253-BA68-CC5D7316A617}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37CAA28C-40BE-4253-BA68-CC5D7316A617}.Release|Any CPU.Build.0 = Release|Any CPU
{056ED976-618F-4A3E-910E-AA25230C2296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{056ED976-618F-4A3E-910E-AA25230C2296}.Debug|Any CPU.Build.0 = Debug|Any CPU
{056ED976-618F-4A3E-910E-AA25230C2296}.Release|Any CPU.ActiveCfg = Release|Any CPU
{056ED976-618F-4A3E-910E-AA25230C2296}.Release|Any CPU.Build.0 = Release|Any CPU
{B160F0E7-799A-4EB9-92B8-D71623C7674A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B160F0E7-799A-4EB9-92B8-D71623C7674A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B160F0E7-799A-4EB9-92B8-D71623C7674A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B160F0E7-799A-4EB9-92B8-D71623C7674A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 2 additions & 0 deletions build-tools/scripts/RunTests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
<ItemGroup>
<_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\Xamarin.Android.Build.Tests.dll" />
<_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\CodeBehind\CodeBehindUnitTests.dll" />
<_TestAssembly Include="$(_TopDir)\bin\Test$(Configuration)\EmbeddedDSO\EmbeddedDSOUnitTests.dll" />
<_ApkTestProject Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\CodeGen-Binding\Xamarin.Android.JcwGen-Tests\Xamarin.Android.JcwGen-Tests.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\CodeGen-MkBundle\Xamarin.Android.MakeBundle-Tests\Xamarin.Android.MakeBundle-Tests.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\locales\Xamarin.Android.Locale-Tests\Xamarin.Android.Locale-Tests.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\BCL-Tests\Xamarin.Android.Bcl-Tests\Xamarin.Android.Bcl-Tests.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\Xamarin.Forms-Performance-Integration\Droid\Xamarin.Forms.Performance.Integration.Droid.csproj" />
<_ApkTestProject Include="$(_TopDir)\tests\EmbeddedDSOs\EmbeddedDSO\EmbeddedDSO.csproj" />
<_ApkTestProjectAot Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" />
<_ApkTestProjectBundle Include="$(_TopDir)\src\Mono.Android\Test\Mono.Android-Tests.csproj" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/AndroidZipAlign.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected override string GenerateCommandLineCommands ()
string sourceFilename = Path.GetFileNameWithoutExtension (Source.ItemSpec);
if (sourceFilename.EndsWith (strSignedUnaligned))
sourceFilename = sourceFilename.Remove (sourceFilename.Length - strSignedUnaligned.Length);
return string.Format ("{0} \"{1}\" \"{2}{3}{4}-Signed.apk\"",
return string.Format ("-p {0} \"{1}\" \"{2}{3}{4}-Signed.apk\"",
Alignment, Source.ItemSpec, DestinationDirectory.ItemSpec, Path.DirectorySeparatorChar, sourceFilename);
}

Expand Down
15 changes: 4 additions & 11 deletions src/monodroid/jni/dylib-mono.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,18 @@ void monodroid_dylib_mono_free (struct DylibMono *mono_imports)
free (mono_imports);
}

int monodroid_dylib_mono_init (struct DylibMono *mono_imports, const char *libmono_path)
int monodroid_dylib_mono_init (struct DylibMono *mono_imports, void *libmono_handle)
{
int symbols_missing = FALSE;

if (mono_imports == NULL)
return FALSE;

memset (mono_imports, 0, sizeof (*mono_imports));

/*
* We need to use RTLD_GLOBAL so that libmono-profiler-log.so can resolve
* symbols against the Mono library we're loading.
*/
mono_imports->dl_handle = dlopen (libmono_path, RTLD_LAZY | RTLD_GLOBAL);

if (!mono_imports->dl_handle) {
if (libmono_handle == NULL)
return FALSE;
}

memset (mono_imports, 0, sizeof (*mono_imports));
mono_imports->dl_handle = libmono_handle;
mono_imports->version = sizeof (*mono_imports);

log_info (LOG_DEFAULT, "Loading Mono symbols...");
Expand Down
2 changes: 1 addition & 1 deletion src/monodroid/jni/dylib-mono.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,6 @@ struct DylibMono {

MONO_API struct DylibMono* monodroid_dylib_mono_new (const char *libmono_path);
MONO_API void monodroid_dylib_mono_free (struct DylibMono *mono_imports);
int monodroid_dylib_mono_init (struct DylibMono *mono_imports, const char *libmono_path);
int monodroid_dylib_mono_init (struct DylibMono *mono_imports, void *libmono_handle);

#endif /* INC_MONODROID_DYLIB_MONO_H */
Loading

0 comments on commit 95ca102

Please sign in to comment.