-
Notifications
You must be signed in to change notification settings - Fork 531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these so much larger now?
Should we actually remove settings like AndroidUseAssemblyStore=false
and test the defaults instead?
android/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
Line 157 in 0297b5c
proj.SetProperty ("AndroidUseAssemblyStore", "False"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 taskCode
parameter, allowing us tolog.LogCodedError(taskCode, …)
, or - Replace the
TaskLoggingHelper
parameter with anAndroidTask
parameter, allowing this method access to.Log
and.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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a .hh.in
file instead of #ifdef
s or if constexpr
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.