[native] Real shared libraries in APK /lib directories#9154
Conversation
e3b5ff1 to
0b83566
Compare
59c52cb to
09114b5
Compare
e8016b7 to
93d5741
Compare
| Console.WriteLine (message); | ||
| } else { | ||
| Console.WriteLine ($"Extracted content from ELF image '{path}'"); | ||
| } |
There was a problem hiding this comment.
Should these be going to an MSBuild log instead of Console? They won't appear in log files.
There was a problem hiding this comment.
I'm on a fence about these. I find them useful when looking for reasons why a test failed because of changes in the assembly store etc formats when I run the tests locally, but they're not going to be useful in general so they'd just pollute logs for no real gain.
There was a problem hiding this comment.
The only downside is they will be emitting stuff on users machines as well, especially those building on VScode or CLI. And no amount of setting the log verbosity will get rid of them.
There was a problem hiding this comment.
They are only used in tests, this code doesn't run as part of a regular app build.
| "lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": { | ||
| "Size": 1114 | ||
| "Size": 18208 | ||
| }, | ||
| "lib/arm64-v8a/lib_Java.Interop.dll.so": { | ||
| "Size": 69368 | ||
| "Size": 86456 | ||
| }, | ||
| "lib/arm64-v8a/lib_Mono.Android.dll.so": { | ||
| "Size": 98691 | ||
| "Size": 115720 | ||
| }, |
There was a problem hiding this comment.
Why are these so much larger now?
Should we actually remove settings like AndroidUseAssemblyStore=false and test the defaults instead?
There was a problem hiding this comment.
They are larger because they not only have the real ELF header and sections, they are also aligned to the 16k boundary between sections.
A far as defaults are concerned, there are other tests which use them, so I think it's fine to leave the assembly store disabled here.
93d5741 to
6e6e24c
Compare
| onOutput?.Invoke (s, e); | ||
| stdoutLines.Add (e.Data); | ||
| } else | ||
| stdout_completed.Set (); |
There was a problem hiding this comment.
e.Data is null when the process exits?! I was not aware of that, nor do I see that documented at Process.OutputDataReceived.
The following code also feels odd, as it suggests that events may still be raised after proc.WaitForExit(), hence the need for e.g. stderr_completed.WaitOne(), which just feels odd.
There was a problem hiding this comment.
This pattern was established some 10-11 years ago, we've seen all these conditions happen along the years (especially when I worked with the Xamarin Installer, but also in Mono Develop/Xamarin Studio). e.Data == null is documented here:
When the redirected stream is closed, a null line is sent to the event handler. Ensure your event handler checks the [Data](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.datareceivedeventargs.data?view=net-8.0)
property appropriately before accessing it. For example, you can use the static method [String.IsNullOrEmpty](https://learn.microsoft.com/en-us/dotnet/api/system.string.isnullorempty?view=net-8.0) to validate
the [Data](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.datareceivedeventargs.data?view=net-8.0) property in your event handler.
Process exiting means only that the process itself is gone, its output streams may still have data that needs to be piped to us, hence the need to wait.
|
|
||
| if (proc.ExitCode != 0) { | ||
| var sb = MergeStdoutAndStderrMessages (stdoutLines, stderrLines); | ||
| log.LogError ($"Command '{psi.FileName} {psi.Arguments}' failed.\n{sb.ToString ()}"); |
There was a problem hiding this comment.
I think there should be a slight re-think on this, as this will be an uncoded error message, if and when it happens.
"Better" might be to do one of two things:
- Add a
string taskCodeparameter, allowing us tolog.LogCodedError(taskCode, …), or - Replace the
TaskLoggingHelperparameter with anAndroidTaskparameter, allowing this method access to.Logand.TaskPrefix. We'd still have to "concoct" an error code --${task.TaskPrefix}0000? -- but it's better than no code at all, as error codes are logged via telemetry.
TODO: config files, runtime config blob
Handling individual assemblies loaded from the filesystem and the apk is next.
...let's see what breaks on CI
6e6e24c to
d55e91a
Compare
| elf_header->e_ident[EI_MAG1] != ELFMAG1 || | ||
| elf_header->e_ident[EI_MAG2] != ELFMAG2 || | ||
| elf_header->e_ident[EI_MAG3] != ELFMAG3) { | ||
| log_debug (LOG_ASSEMBLY, "Not an ELF image"); |
There was a problem hiding this comment.
Could this include the filename/etc. of what exactly is "not an ELF image"?
| namespace xamarin::android { | ||
| struct ArchiveDSOStubConfig | ||
| { | ||
| static inline constexpr size_t PayloadSectionAlignment = @ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT@; |
There was a problem hiding this comment.
Why a .hh.in file instead of #ifdefs or if constexpr?
There was a problem hiding this comment.
Fields are assigned values that are detected by CMakeLists.txt after the stub is built. The stub size, layout etc may differ even within the same target platform if the compiler (e.g. a flag change which affects sections that are present/absent) or linker (e.g. order of section and, thus, size of the entire stub) change, and we must absolutely have the correct values here. Getting these values require reading the stub ELF file and doing some calculations after the payload section is added, since the stub itself is compiled without it (we want to ship the stub without the payload section) - this is what code in CMakeLists.txt does.
|
Draft WIP commit message: Context: https://developer.android.com/guide/practices/page-sizes
Context: https://android-developers.googleblog.com/2024/08/adding-16-kb-page-size-to-android.html
Context: 86260ed36dfe1a90c8ed6a2bb1cd0607d637f403
Android will "soon" require that native libraries on 64-bit platforms
(arm64-v8a, x64) be aligned on a 16KB page size. Our *suspicion* is
that this will in turn require that anything stored within
`lib/ABI/lib*.so` entries within the `.apk` *must also* be *real*
native libraries.
Currently that is *not* the case (86260ed3).
Bundle non-native library data into real native libraries by using
[`llvm-objcopy --add-section payload=FILE`][0], which adds an ELF
section named `payload` with the contents of `FILE`, a'la:
cp path/to/libarchive-dso-stub.so libarc.bin.so
llvm-objcopy --add-section payload=rc.bin libarc.bin.so && \
llvm-objcopy --set-section-flags payload=readonly,data --set-section-alignment payload=ALIGNMENT libarc.bin.so
Note the use of `llvm-objcopy --set-section-alignment` which allows
the resulting `lib*.so` to have e.g. 16KB alignment.
If any of the the `llvm-objcopy` commands fail, an XA0142 error will
be generated:
Command 'llvm-objcopy …' failed.
<stdout and stderr from failing command…>
To make this work:
1. Build a "stub" `libarchive-dso-stub.so` for each supported ABI,
and include this in the .NET for Android workload.
2. Update the .NET for Android workload to now include the
OS-specfic `llvm-objcopy` command, built separately from
[dotnet/android-native-tools][1]; see also e.g. 3eec7e9f.
Note: `*.apkdesc` files show size increases, because assembly &
related data are now wrapped in a 16KB-aligned `.so`, which causes
each such entry to increase in size by ~16KB.
[0]: https://www.llvm.org/docs/CommandGuide/llvm-objcopy.html
[1]: https://github.com/dotnet/android-native-tools |
Fix indentation
…)" This reverts commit 5ebcb1d.
)" This reverts commit b795a65.
)" This reverts commit b795a65.
)" This reverts commit b795a65.
No description provided.