blob: a0a0d379ba8b63ab1d955777463af0722e3f2b29 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/android/library_loader/library_prefetcher.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <csignal>
#include <cstddef>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/bits.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
#include "base/android/orderfile/orderfile_instrumentation.h" // nogncheck
#endif
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
namespace base {
namespace android {
namespace {
#if !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
// The binary is aligned to a minimum 16K page size on AArch64 Android, else 4K.
#if defined(ARCH_CPU_ARM64)
constexpr size_t kPageSize = 16384;
#else
constexpr size_t kPageSize = 4096;
#endif
// Reads a byte per page between |start| and |end| to force it into the page
// cache.
// Heap allocations, syscalls and library functions are not allowed in this
// function.
// Returns true for success.
#if defined(ADDRESS_SANITIZER)
// Disable AddressSanitizer instrumentation for this function. It is touching
// memory that hasn't been allocated by the app, though the addresses are
// valid. Furthermore, this takes place in a child process. See crbug.com/653372
// for the context.
__attribute__((no_sanitize_address))
#endif
void Prefetch(size_t start, size_t end) {
unsigned char* start_ptr = reinterpret_cast<unsigned char*>(start);
unsigned char* end_ptr = reinterpret_cast<unsigned char*>(end);
[[maybe_unused]] unsigned char dummy = 0;
for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
// Volatile is required to prevent the compiler from eliminating this
// loop.
dummy ^= *static_cast<volatile unsigned char*>(ptr);
}
}
// These values were used in the past for recording
// "LibraryLoader.PrefetchDetailedStatus".
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. See PrefetchStatus in enums.xml.
enum class PrefetchStatus {
kSuccess = 0,
kWrongOrdering = 1,
kForkFailed = 2,
kChildProcessCrashed = 3,
kChildProcessKilled = 4,
kMaxValue = kChildProcessKilled
};
PrefetchStatus ForkAndPrefetch() {
if (!IsOrderingSane()) {
LOG(WARNING) << "Incorrect code ordering";
return PrefetchStatus::kWrongOrdering;
}
pid_t pid = fork();
if (pid == 0) {
// Android defines the background priority to this value since at least 2009
// (see Process.java).
constexpr int kBackgroundPriority = 10;
setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
// |kStartOfText| may not be at the beginning of a page, since .plt can be
// before it, yet in the same mapping for instance.
size_t text_start_page = kStartOfText - kStartOfText % kPageSize;
// Set the end to the page on which the beginning of the last symbol is. The
// actual symbol may spill into the next page by a few bytes, but this is
// outside of the executable code range anyway.
size_t text_end_page = bits::AlignUp(kEndOfText, kPageSize);
size_t ordered_start_page =
kStartOfOrderedText - kStartOfOrderedText % kPageSize;
// kEndOfUnorderedText is not considered ordered, but the byte immediately
// before is considered ordered and so can not be contained in the start
// page.
size_t ordered_end_page = bits::AlignUp(kEndOfOrderedText, kPageSize);
// Fetch the ordered section first.
Prefetch(ordered_start_page, ordered_end_page);
Prefetch(text_start_page, text_end_page);
// _exit() doesn't call the atexit() handlers.
_exit(EXIT_SUCCESS);
} else {
if (pid < 0) {
return PrefetchStatus::kForkFailed;
}
int status;
const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
if (result == pid) {
if (WIFEXITED(status)) {
return PrefetchStatus::kSuccess;
}
if (WIFSIGNALED(status)) {
int signal = WTERMSIG(status);
switch (signal) {
case SIGSEGV:
case SIGBUS:
return PrefetchStatus::kChildProcessCrashed;
case SIGKILL:
case SIGTERM:
default:
return PrefetchStatus::kChildProcessKilled;
}
}
}
// Should not happen. Per man waitpid(2), errors are:
// - EINTR: handled.
// - ECHILD if the process doesn't have an unwaited-for child with this PID.
// - EINVAL.
return PrefetchStatus::kChildProcessKilled;
}
}
#endif // !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
} // namespace
// static
void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
// Avoid forking with orderfile instrumentation because the child process
// would create a dump as well.
return;
#else
base::TimeTicks start_time = base::TimeTicks::Now();
PrefetchStatus status = ForkAndPrefetch();
base::UmaHistogramMediumTimes("Android.LibraryLoader.Prefetch.Duration",
base::TimeTicks::Now() - start_time);
base::UmaHistogramEnumeration("Android.LibraryLoader.Prefetch.Status",
status);
if (status != PrefetchStatus::kSuccess) {
LOG(WARNING) << "Cannot prefetch the library. status = "
<< static_cast<int>(status);
}
#endif // BUILDFLAG(ORDERFILE_INSTRUMENTATION)
}
} // namespace android
} // namespace base
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)