Files
sousa-gecko/tools/profiler/lul/AutoObjectMapper.cpp
T
Nazım Can Altınova 0bc1946f4e Bug 2047859 - Read LUL unwind info for libraries loaded from inside the APK on Android r=mstange,profiler-reviewers
After legacy packaging was disabled (bug 1867280), native libraries like
libxul.so are loaded directly from inside the APK/AAB instead of being
extracted uncompressed to disk. The dynamic linker reports their path
using the "<apk>!/<entry>" syntax (for example:
".../split_config.arm64_v8a.apk!/lib/ arm64-v8a/libxul.so"), which is
not a real filesystem path.

Bug 1773313 already moved breakpadId/codeId extraction to read from
memory, so that metadata was correct. But LUL still obtained unwinding
information by `open()`ing the library file from disk. With the
"<apk>!/" path, that `open()` fails, and because of that,
`NotifyAfterMap()` never gets called for libxul and libmozglue, and
stack walking gets broken due to this.

The library is stored uncompressed and page-aligned inside the APK, so
it can still be mapped by opening the APK and mapping at the library's
offset within it. It is the file offset of the mapping that backs the
library's first loaded page, which we recover from /proc/self/maps.

If the offset can't be resolved we fall back to NotifyExecutableArea()
so that stack scanning still works.

Differential Revision: https://phabricator.services.mozilla.com/D307383
2026-06-18 09:53:34 +00:00

79 lines
2.2 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mozilla/Assertions.h"
#include "mozilla/Sprintf.h"
#include "mozilla/ProfilerPlatformMacros.h"
#include "AutoObjectMapper.h"
// A helper function for creating failure error messages in
// AutoObjectMapper*::Map.
static void failedToMessage(void (*aLog)(const char*), const char* aHowFailed,
std::string aFileName) {
char buf[300];
SprintfLiteral(buf, "AutoObjectMapper::Map: Failed to %s \'%s\'", aHowFailed,
aFileName.c_str());
buf[sizeof(buf) - 1] = 0;
aLog(buf);
}
AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void (*aLog)(const char*))
: mImage(nullptr), mSize(0), mLog(aLog), mIsMapped(false) {}
AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() {
if (!mIsMapped) {
// There's nothing to do.
MOZ_ASSERT(!mImage);
MOZ_ASSERT(mSize == 0);
return;
}
MOZ_ASSERT(mSize > 0);
// The following assertion doesn't necessarily have to be true,
// but we assume (reasonably enough) that no mmap facility would
// be crazy enough to map anything at page zero.
MOZ_ASSERT(mImage);
munmap(mImage, mSize);
}
bool AutoObjectMapperPOSIX::Map(/*OUT*/ void** start, /*OUT*/ size_t* length,
std::string fileName, uint64_t offset) {
MOZ_ASSERT(!mIsMapped);
int fd = open(fileName.c_str(), O_RDONLY);
if (fd == -1) {
failedToMessage(mLog, "open", fileName);
return false;
}
struct stat st;
int err = fstat(fd, &st);
size_t sz = (err == 0) ? st.st_size : 0;
if (err != 0 || sz == 0 || offset >= sz) {
failedToMessage(mLog, "fstat", fileName);
close(fd);
return false;
}
size_t mapSize = sz - offset;
void* image = mmap(nullptr, mapSize, PROT_READ, MAP_SHARED, fd, offset);
if (image == MAP_FAILED) {
failedToMessage(mLog, "mmap", fileName);
close(fd);
return false;
}
close(fd);
mIsMapped = true;
mImage = *start = image;
mSize = *length = mapSize;
return true;
}