diff --git a/client/crashpad_client.h b/client/crashpad_client.h index ea0e7b6d..8bf43acd 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -188,6 +188,97 @@ class CrashpadClient { const std::map& annotations, const std::vector& 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* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& 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* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket); #endif // OS_ANDROID || DOXYGEN #if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 556a91fc..fe5798a5 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -77,6 +77,36 @@ std::vector BuildAppProcessArgs( return argv; } +std::vector 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& annotations, + const std::vector& arguments, + int socket) { + std::vector 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 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* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments) { + std::vector 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* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector 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 diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 32d4d6c1..bbd68ab1 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -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") { diff --git a/handler/handler_main.cc b/handler/handler_main.cc index b3e0a68a..31686b3e 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -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) { diff --git a/handler/linux/handler_trampoline.cc b/handler/linux/handler_trampoline.cc new file mode 100644 index 00000000..34535631 --- /dev/null +++ b/handler/linux/handler_trampoline.cc @@ -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 +#include +#include + +// 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 ", 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(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); +}