android: Add methods to start handler with /system/bin/linker

Starting with Android Q, Bionic's linker will support loading
executables from an APK, replacing the /system/bin/app_process
workaround.

libhandler_trampoline.so is a small executable, which `dlopen()`s
the handler code from another native library allowing
de-duplicating shared code with that library without having that
library available for a more direct link time dependency.

Bug: 928422
Change-Id: Ib126b8fca6005a34b9e4ef103eb1383dc0c554ea
Reviewed-on: https://chromium-review.googlesource.com/c/1477336
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Joshua Peraza 2019-02-21 17:26:25 -08:00 committed by Commit Bot
parent dc17650336
commit 99bf283e54
5 changed files with 248 additions and 0 deletions

View File

@ -188,6 +188,97 @@ class CrashpadClient {
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket);
//! \brief Installs a signal handler to start a Crashpad handler process by
//! loading it with `/system/bin/linker`.
//!
//! This method is only supported by Android Q+.
//!
//! \param[in] handler_trampoline The path to a Crashpad handler trampoline
//! executable, possibly located within an apk, e.g.
//! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
//! \param[in] handler_library The name of a library exporting the symbol
//! `CrashpadHandlerMain()`. The path to this library must be present in
//! `LD_LIBRARY_PATH`.
//! \param[in] is_64_bit `true` if \a handler_trampoline and \a
//! handler_library are 64-bit objects. They must have the same bitness.
//! \param[in] env A vector of environment variables of the form `var=value`
//! defining the environment in which to execute `app_process`. If this
//! value is `nullptr`, the application's environment at the time of the
//! crash will be used.
//! \param[in] database The path to a Crashpad database. The handler will be
//! started with this path as its `--database` argument.
//! \param[in] metrics_dir The path to an already existing directory where
//! metrics files can be stored. The handler will be started with this
//! path as its `--metrics-dir` argument.
//! \param[in] url The URL of an upload server. The handler will be started
//! with this URL as its `--url` argument.
//! \param[in] annotations Process annotations to set in each crash report.
//! The handler will be started with an `--annotation` argument for each
//! element in this map.
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
//! Arguments passed in other parameters and arguments required to perform
//! the handshake are the responsibility of this method, and must not be
//! specified in this parameter.
//!
//! \return `true` on success, `false` on failure with a message logged.
static bool StartHandlerWithLinkerAtCrash(
const std::string& handler_trampoline,
const std::string& handler_library,
bool is_64_bit,
const std::vector<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments);
//! \brief Starts a Crashpad handler process with an initial client by loading
//! it with `/system/bin/linker`.
//!
//! This method is only supported by Android Q+.
//!
//! \param[in] handler_trampoline The path to a Crashpad handler trampoline
//! executable, possibly located within an apk, e.g.
//! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
//! \param[in] handler_library The name of a library exporting the symbol
//! `CrashpadHandlerMain()`. The path to this library must be present in
//! `LD_LIBRARY_PATH`.
//! \param[in] is_64_bit `true` if \a handler_trampoline and \a
//! handler_library are 64-bit objects. They must have the same bitness.
//! \param[in] env A vector of environment variables of the form `var=value`
//! defining the environment in which to execute `app_process`. If this
//! value is `nullptr`, the application's current environment will be
//! used.
//! \param[in] database The path to a Crashpad database. The handler will be
//! started with this path as its `--database` argument.
//! \param[in] metrics_dir The path to an already existing directory where
//! metrics files can be stored. The handler will be started with this
//! path as its `--metrics-dir` argument.
//! \param[in] url The URL of an upload server. The handler will be started
//! with this URL as its `--url` argument.
//! \param[in] annotations Process annotations to set in each crash report.
//! The handler will be started with an `--annotation` argument for each
//! element in this map.
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
//! Arguments passed in other parameters and arguments required to perform
//! the handshake are the responsibility of this method, and must not be
//! specified in this parameter.
//! \param[in] socket The server end of a socket pair. The client end should
//! be used with an ExceptionHandlerClient.
//!
//! \return `true` on success, `false` on failure with a message logged.
static bool StartHandlerWithLinkerForClient(
const std::string& handler_trampoline,
const std::string& handler_library,
bool is_64_bit,
const std::vector<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket);
#endif // OS_ANDROID || DOXYGEN
#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN

View File

@ -77,6 +77,36 @@ std::vector<std::string> BuildAppProcessArgs(
return argv;
}
std::vector<std::string> BuildArgsToLaunchWithLinker(
const std::string& handler_trampoline,
const std::string& handler_library,
bool is_64_bit,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket) {
std::vector<std::string> argv;
if (is_64_bit) {
argv.push_back("/system/bin/linker64");
} else {
argv.push_back("/system/bin/linker");
}
argv.push_back(handler_trampoline);
argv.push_back(handler_library);
std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
base::FilePath(), database, metrics_dir, url, annotations, arguments);
if (socket != kInvalidFileHandle) {
handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
}
argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
return argv;
}
#endif // OS_ANDROID
// Launches a single use handler to snapshot this process.
@ -251,6 +281,61 @@ bool CrashpadClient::StartJavaHandlerForClient(
return DoubleForkAndExec(argv, env, socket, false, nullptr);
}
// static
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
const std::string& handler_trampoline,
const std::string& handler_library,
bool is_64_bit,
const std::vector<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments) {
std::vector<std::string> argv =
BuildArgsToLaunchWithLinker(handler_trampoline,
handler_library,
is_64_bit,
database,
metrics_dir,
url,
annotations,
arguments,
kInvalidFileHandle);
auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, env)) {
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
}
// static
bool CrashpadClient::StartHandlerWithLinkerForClient(
const std::string& handler_trampoline,
const std::string& handler_library,
bool is_64_bit,
const std::vector<std::string>* env,
const base::FilePath& database,
const base::FilePath& metrics_dir,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
int socket) {
std::vector<std::string> argv =
BuildArgsToLaunchWithLinker(handler_trampoline,
handler_library,
is_64_bit,
database,
metrics_dir,
url,
annotations,
arguments,
socket);
return DoubleForkAndExec(argv, env, socket, false, nullptr);
}
#endif
// static

View File

@ -166,6 +166,21 @@ if (crashpad_is_android) {
"$root_out_dir/libcrashpad_handler.so",
]
}
crashpad_executable("crashpad_handler_trampoline") {
set_sources_assignment_filter([])
output_name = "libcrashpad_handler_trampoline.so"
sources = [
"linux/handler_trampoline.cc",
]
ldflags = [ "-llog" ]
if (crashpad_is_in_chromium) {
no_default_deps = true
}
}
}
crashpad_executable("crashpad_handler_test_extended_handler") {

View File

@ -497,6 +497,18 @@ class ScopedStoppable {
} // namespace
#if defined(OS_ANDROID)
extern "C" {
__attribute__((visibility("default"), used)) int CrashpadHandlerMain(
int argc,
char* argv[]) {
return HandlerMain(argc, argv, nullptr);
}
} // extern "C"
#endif // OS_ANDROID
int HandlerMain(int argc,
char* argv[],
const UserStreamDataSources* user_stream_sources) {

View File

@ -0,0 +1,45 @@
// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <android/log.h>
#include <dlfcn.h>
#include <stdlib.h>
// The first argument passed to the trampoline is the name of the native library
// exporting the symbol `CrashpadHandlerMain`. The remaining arguments are the
// same as for `HandlerMain()`.
int main(int argc, char* argv[]) {
static constexpr char kTag[] = "crashpad";
if (argc < 2) {
__android_log_print(ANDROID_LOG_FATAL, kTag, "usage: %s <path>", argv[0]);
return EXIT_FAILURE;
}
void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
__android_log_print(ANDROID_LOG_FATAL, kTag, "dlopen: %s", dlerror());
return EXIT_FAILURE;
}
using MainType = int (*)(int, char*[]);
MainType crashpad_main =
reinterpret_cast<MainType>(dlsym(handle, "CrashpadHandlerMain"));
if (!crashpad_main) {
__android_log_print(ANDROID_LOG_FATAL, kTag, "dlsym: %s", dlerror());
return EXIT_FAILURE;
}
return crashpad_main(argc - 1, argv + 1);
}