Skip to content

Commit

Permalink
[native] Let Android know what is our reason to abort() (#9314)
Browse files Browse the repository at this point in the history
[Android NDK r10c (2014)][0]:

> Added the following functions to all architectures:
> `android_set_abort_message`, `posix_fadvise`, `posix_fadvise64`,
> `pthread_gettid_np`.

There doesn't appear to be any "proper" documentation for
[`android_set_abort_message()`][1] (404), but it is *mentioned* in
the [Logging][2] documentation, e.g.
[`__android_log_call_aborter()`][3]:

> **abort_message**: an additional message supplied when aborting,
> for example this is used to call [android_set_abort_message()][1]
> in [__android_log_default_aborter()][3].

`android_set_abort_message()` allows the caller to set a text message
indicating the reason why the application is aborting its execution
using the **abort**(3) function, since the call doesn't accept any
parameters.  The message is then used in the native stack trace as
well as in the application tombstone, which may be helpful for us
when reading crash reports which contain just the stack trace and no
context information.

Given the following code:

	Helpers::abort_application (LOG_ASSEMBLY, "Testing abort stuff");

We will see the following logged in logcat:

	F monodroid-assembly: Testing abort stuff
	F monodroid-assembly: Abort at monodroid-glue.cc:825:2 ('void xamarin::android::internal::MonodroidRuntime::init_android_runtime(JNIEnv *, jclass, jobject)')

	F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 8803 (XAPerfTest.net9), pid 8803 (XAPerfTest.net9)
	I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto
	I tombstoned: received crash request for pid 8803
	I crash_dump64: performing dump of process 8803 (target tid = 8803)
	F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
	F DEBUG   : Build fingerprint: 'google/raven/raven:14/AP2A.240805.005.F1/12043167:user/release-keys'
	F DEBUG   : Revision: 'MP1.0'
	F DEBUG   : ABI: 'arm64'
	F DEBUG   : Timestamp: 2024-09-24 12:00:57.245408791+0200
	F DEBUG   : Process uptime: 1s
	F DEBUG   : Cmdline: com.xamarin.XAPerfTest.net9
	F DEBUG   : pid: 8803, tid: 8803, name: XAPerfTest.net9  >>> com.xamarin.XAPerfTest.net9 <<<
	F DEBUG   : uid: 10456
	F DEBUG   : tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)
	F DEBUG   : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
	F DEBUG 👉: Abort message: 'Testing abort stuff'
	F DEBUG   :     x0  0000000000000000  x1  0000000000002263  x2  0000000000000006  x3  0000007fc7ed5530
	F DEBUG   :     x4  3139343137396262  x5  3139343137396262  x6  3139343137396262  x7  7f7f7f7f7f7f7f7f
	F DEBUG   :     x8  00000000000000f0  x9  000000721c528350  x10 0000000000000001  x11 000000721c579170
	F DEBUG   :     x12 0000007fc7ed3e50  x13 0000000000000088  x14 0000007fc7ed5068  x15 000000057ae2d47e
	F DEBUG   :     x16 000000721c5dffd0  x17 000000721c5cb560  x18 0000007230490000  x19 0000000000002263
	F DEBUG   :     x20 0000000000002263  x21 00000000ffffffff  x22 0000006f69e1d7d6  x23 0000007fc7ed58a4
	F DEBUG   :     x24 0000007fc7ed58c8  x25 0000000000000000  x26 0000000000000000  x27 b400007218a0a060
	F DEBUG   :     x28 0000000000000000  x29 0000007fc7ed55b0
	F DEBUG   :     lr  000000721c5628b8  sp  0000007fc7ed5510  pc  000000721c5628e4  pst 0000000000001000

The rest of stack trace is omitted for brevity.

We log the indicated message before calling `abort()`, but it also
appears on the `Abort message:` line in the stack trace; see 👉.

Messages provided to `android_set_abort_message()` appear to have an
undocumented length limit of 120 characters, so messages should
"front-load" important information.

Additionally, don't log the full path to the source file containing
the abort call, a file name is just enough.

[0]: https://developer.android.com/ndk/downloads/revision_history
[1]: https://developer.android.com/ndk/reference/set/abort-message-8h#set__abort__message_8h_1ac483b2fcb74191566f18476ce3280c9c
[2]: https://developer.android.com/ndk/reference/group/logging
[3]: https://developer.android.com/ndk/reference/group/logging#__android_log_call_aborter
  • Loading branch information
grendello authored Oct 14, 2024
1 parent c2a9ac6 commit 594d090
Show file tree
Hide file tree
Showing 18 changed files with 316 additions and 125 deletions.
12 changes: 8 additions & 4 deletions src/native/monodroid/debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,13 @@ Debug::start_debugging_and_profiling ()
if (AndroidSystem::monodroid_get_system_property (SharedConstants::DEBUG_MONO_CONNECT_PROPERTY, &connect_args) > 0) {
DebuggerConnectionStatus res = start_connection (connect_args);
if (res == DebuggerConnectionStatus::Error) {
log_fatal (LOG_DEBUGGER, "Could not start a connection to the debugger with connection args '%s'.", connect_args);
Helpers::abort_application ();
Helpers::abort_application (
LOG_DEBUGGER,
Util::monodroid_strdup_printf (
"Connection to debugger failed. Args: %s",
connect_args
)
);
} else if (res == DebuggerConnectionStatus::Connected) {
/* Wait for XS to configure debugging/profiling */
gettimeofday(&wait_tv, nullptr);
Expand Down Expand Up @@ -604,8 +609,7 @@ xamarin::android::conn_thread (void *arg)
Debug *instance = static_cast<Debug*> (arg);
res = instance->handle_server_connection ();
if (res && res != 3) {
log_fatal (LOG_DEBUGGER, "Error communicating with the IDE, exiting...");
Helpers::abort_application ();
Helpers::abort_application (LOG_DEBUGGER, "Error communicating with the IDE, exiting...");
}

return nullptr;
Expand Down
97 changes: 78 additions & 19 deletions src/native/monodroid/embedded-assemblies-zip.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector<uint8

log_debug (LOG_ASSEMBLY, "%s entry: %s", state.file_name, entry_name.get () == nullptr ? "unknown" : entry_name.get ());
if (!result || entry_name.empty ()) {
log_fatal (LOG_ASSEMBLY, "Failed to read Central Directory info for entry %u in APK file %s", entry_index, state.file_name);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to read Central Directory info for entry %u in APK %s",
entry_index,
state.file_name
)
);
}

if (!zip_adjust_data_offset (state.file_fd, state)) {
log_fatal (LOG_ASSEMBLY, "Failed to adjust data start offset for entry %u in APK file %s", entry_index, state.file_name);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to adjust data start offset for entry %u in APK %s",
entry_index,
state.file_name
)
);
}

log_debug (LOG_ASSEMBLY, " ZIP: local header offset: %u; data offset: %u; file size: %u", state.local_header_offset, state.data_offset, state.file_size);
Expand Down Expand Up @@ -61,9 +73,15 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector<uint8

// assemblies must be 16-byte or 4-byte aligned, or Bad Things happen
if (((state.data_offset & 0xf) != 0) || ((state.data_offset & 0x3) != 0)) {
log_fatal (LOG_ASSEMBLY, "Assembly '%s' is located at bad offset %lu within the .apk", entry_name.get (), state.data_offset);
log_fatal (LOG_ASSEMBLY, "You MUST run `zipalign` on %s to align it on 4 or 16 bytes ", strrchr (state.file_name, '/') + 1);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Assembly '%s' is at bad offset %lu in the APK (not aligned to 4 or 16 bytes). 'zipalign' MUST be used on %s to align it properly",
entry_name.get (),
state.data_offset,
strrchr (state.file_name, '/') + 1
)
);
}

return true;
Expand Down Expand Up @@ -161,8 +179,13 @@ inline void
EmbeddedAssemblies::map_assembly_store (dynamic_local_string<SENSIBLE_PATH_MAX> const& entry_name, ZipEntryLoadState &state) noexcept
{
if (number_of_mapped_assembly_stores > number_of_assembly_store_files) {
log_fatal (LOG_ASSEMBLY, "Too many assembly stores. Expected at most %u", number_of_assembly_store_files);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Too many assembly stores. Expected at most %u",
number_of_assembly_store_files
)
);
}

int fd;
Expand Down Expand Up @@ -192,13 +215,25 @@ EmbeddedAssemblies::map_assembly_store (dynamic_local_string<SENSIBLE_PATH_MAX>
auto header = static_cast<AssemblyStoreHeader*>(payload_start);

if (header->magic != ASSEMBLY_STORE_MAGIC) {
log_fatal (LOG_ASSEMBLY, "Assembly store '%s' is not a valid .NET for Android assembly store file", entry_name.get ());
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Assembly store '%s' is not a valid .NET for Android assembly store file",
entry_name.get ()
)
);
}

if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) {
log_fatal (LOG_ASSEMBLY, "Assembly store '%s' uses format version 0x%x, instead of the expected 0x%x", entry_name.get (), header->version, ASSEMBLY_STORE_FORMAT_VERSION);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Assembly store '%s' uses format version 0x%x, instead of the expected 0x%x",
entry_name.get (),
header->version,
ASSEMBLY_STORE_FORMAT_VERSION
)
);
}

constexpr size_t header_size = sizeof(AssemblyStoreHeader);
Expand Down Expand Up @@ -272,8 +307,13 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
uint16_t cd_entries;

if (!zip_read_cd_info (fd, cd_offset, cd_size, cd_entries)) {
log_fatal (LOG_ASSEMBLY, "Failed to read the EOCD record from APK file %s", apk_name);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to read the EOCD record from APK file %s",
apk_name
)
);
}
#ifdef DEBUG
log_info (LOG_ASSEMBLY, "Central directory offset: %u", cd_offset);
Expand All @@ -282,8 +322,16 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
#endif
off_t retval = ::lseek (fd, static_cast<off_t>(cd_offset), SEEK_SET);
if (retval < 0) {
log_fatal (LOG_ASSEMBLY, "Failed to seek to central directory position in the APK file %s. %s (result: %d; errno: %d)", apk_name, std::strerror (errno), retval, errno);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to seek to central directory position in APK: %s. retval=%d errno=%d, File=%s",
std::strerror (errno),
retval,
errno,
apk_name
)
);
}

std::vector<uint8_t> buf (cd_size);
Expand All @@ -298,12 +346,23 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
.local_header_offset = 0,
.data_offset = 0,
.file_size = 0,
.bundled_assemblies_slow_path = false,
.max_assembly_name_size = 0,
.max_assembly_file_name_size = 0,
};

ssize_t nread = read (fd, buf.data (), static_cast<read_count_type>(buf.size ()));
if (static_cast<size_t>(nread) != cd_size) {
log_fatal (LOG_ASSEMBLY, "Failed to read Central Directory from the APK archive %s. %s (nread: %d; errno: %d)", apk_name, std::strerror (errno), nread, errno);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to read Central Directory from APK: %s. nread=%d errno=%d File=%s",
std::strerror (errno),
nread,
errno,
apk_name
)
);
}

if (application_config.have_assembly_store) {
Expand Down
88 changes: 67 additions & 21 deletions src/native/monodroid/embedded-assemblies.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,16 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
auto header = reinterpret_cast<const CompressedAssemblyHeader*>(data);
if (header->magic == COMPRESSED_DATA_MAGIC) {
if (compressed_assemblies.descriptors == nullptr) [[unlikely]] {
log_fatal (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined");
Helpers::abort_application ();
Helpers::abort_application (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined");
}
if (header->descriptor_index >= compressed_assemblies.count) [[unlikely]] {
log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor index %u", header->descriptor_index);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Invalid compressed assembly descriptor index %u",
header->descriptor_index
)
);
}

CompressedAssemblyDescriptor &cad = compressed_assemblies.descriptors[header->descriptor_index];
Expand All @@ -105,14 +109,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
}

if (cad.data == nullptr) [[unlikely]] {
log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor at %u: no data", header->descriptor_index);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Invalid compressed assembly descriptor at %u: no data",
header->descriptor_index
)
);
}

if (header->uncompressed_length != cad.uncompressed_file_size) {
if (header->uncompressed_length > cad.uncompressed_file_size) {
log_fatal (LOG_ASSEMBLY, "Compressed assembly '%s' is larger than when the application was built (expected at most %u, got %u). Assemblies don't grow just like that!", name, cad.uncompressed_file_size, header->uncompressed_length);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Compressed assembly '%s' is larger than when the application was built (expected at most %u, got %u). Assemblies don't grow just like that!",
name,
cad.uncompressed_file_size,
header->uncompressed_length
)
);
} else {
log_debug (LOG_ASSEMBLY, "Compressed assembly '%s' is smaller than when the application was built. Adjusting accordingly.", name);
}
Expand All @@ -123,13 +139,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
int ret = LZ4_decompress_safe (data_start, reinterpret_cast<char*>(cad.data), static_cast<int>(assembly_data_size), static_cast<int>(cad.uncompressed_file_size));

if (ret < 0) {
log_fatal (LOG_ASSEMBLY, "Decompression of assembly %s failed with code %d", name, ret);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Decompression of assembly %s failed with code %d",
name,
ret
)
);
}

if (static_cast<uint64_t>(ret) != cad.uncompressed_file_size) {
log_debug (LOG_ASSEMBLY, "Decompression of assembly %s yielded a different size (expected %lu, got %u)", name, cad.uncompressed_file_size, static_cast<uint32_t>(ret));
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Decompression of assembly %s yielded a different size (expected %lu, got %u)",
name,
cad.uncompressed_file_size,
static_cast<uint32_t>(ret)
)
);
}
cad.loaded = true;
}
Expand Down Expand Up @@ -366,8 +395,14 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string<SENSI
}

if (hash_entry->descriptor_index >= assembly_store.assembly_count) {
log_fatal (LOG_ASSEMBLY, "Invalid assembly descriptor index %u, exceeds the maximum value of %u", hash_entry->descriptor_index, assembly_store.assembly_count - 1);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Invalid assembly descriptor index %u, exceeds the maximum value of %u",
hash_entry->descriptor_index,
assembly_store.assembly_count - 1
)
);
}

AssemblyStoreEntryDescriptor &store_entry = assembly_store.assemblies[hash_entry->descriptor_index];
Expand Down Expand Up @@ -499,8 +534,7 @@ EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nme

// This is a coding error on our part, crash!
if (base == nullptr) {
log_fatal (LOG_ASSEMBLY, "Map address not passed to binary_search");
Helpers::abort_application ();
Helpers::abort_application (LOG_ASSEMBLY, "Map address not passed to binary_search");
}

[[maybe_unused]]
Expand Down Expand Up @@ -855,8 +889,15 @@ EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, size_t size, cons
mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast<off_t>(offsetPage));

if (mmap_info.area == MAP_FAILED) {
log_fatal (LOG_DEFAULT, "Could not `mmap` apk fd %d entry `%s`: %s", fd, filename, strerror (errno));
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Could not mmap APK fd %d: %s; File=%s",
fd,
strerror (errno),
filename
)
);
}

mmap_info.size = offsetSize;
Expand All @@ -876,10 +917,15 @@ EmbeddedAssemblies::gather_bundled_assemblies_from_apk (const char* apk, monodro
int fd;

if ((fd = open (apk, O_RDONLY)) < 0) {
log_error (LOG_DEFAULT, "ERROR: Unable to load application package %s.", apk);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"ERROR: Unable to load application package %s.",
apk
)
);
}
log_info (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd);
log_debug (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd);

zip_load_entries (fd, apk, should_register);
}
Expand Down
10 changes: 8 additions & 2 deletions src/native/monodroid/mono-image-loader.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "xxhash.hh"
#include "search.hh"
#include "strings.hh"
#include "util.hh"

#if defined (RELEASE)
#define USE_CACHE 1
Expand Down Expand Up @@ -116,8 +117,13 @@ namespace xamarin::android::internal {
#if defined (USE_CACHE)
ssize_t index = find_index (hash);
if (index < 0) {
log_fatal (LOG_ASSEMBLY, "Failed to look up image index for hash 0x%zx", hash);
Helpers::abort_application ();
Helpers::abort_application (
LOG_ASSEMBLY,
Util::monodroid_strdup_printf (
"Failed to look up image index for hash 0x%zx",
hash
)
);
}

// We don't need to worry about locking here. Even if we're overwriting an entry just set by another
Expand Down
2 changes: 1 addition & 1 deletion src/native/monodroid/mono-log-adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ MonodroidRuntime::mono_log_handler (const char *log_domain, const char *log_leve

__android_log_write (prio, log_domain, message);
if (fatal) {
Helpers::abort_application ();
Helpers::abort_application (message);
}
}

Expand Down
Loading

0 comments on commit 594d090

Please sign in to comment.