android: add Dlsym() which wraps dlsym

`dlsym` on Android KitKat (4.4.*) raises SIGFPE when searching for
non-existent symbols. This wrapper installs a signal handler prior to
calling `dlsym`.

Bug: crashpad:30
Change-Id: Iee94672d3c11b1fad1b01526eea7df688c0356cb
Reviewed-on: https://chromium-review.googlesource.com/835411
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2017-12-22 15:32:37 -08:00 committed by Commit Bot
parent 7a285816e9
commit 990c6d9cb6
6 changed files with 215 additions and 9 deletions

View File

@ -74,6 +74,8 @@ compat_target("compat") {
if (crashpad_is_android) {
sources += [
"android/dlfcn_internal.cc",
"android/dlfcn_internal.h",
"android/elf.h",
"android/linux/elf.h",
"android/linux/prctl.h",

View File

@ -0,0 +1,166 @@
// Copyright 2017 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 "dlfcn_internal.h"
#include <android/api-level.h>
#include <dlfcn.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/system_properties.h>
#include <unistd.h>
#include <mutex>
namespace crashpad {
namespace internal {
// KitKat supports API levels up to 20.
#if __ANDROID_API__ < 21
namespace {
class ScopedSigactionRestore {
public:
ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {}
~ScopedSigactionRestore() { Reset(); }
bool Reset() {
bool result = true;
if (valid_) {
result = sigaction(signo_, &old_action_, nullptr) == 0;
if (!result) {
PrintErrmsg(errno);
}
}
valid_ = false;
signo_ = -1;
return result;
}
bool ResetAndInstallHandler(int signo,
void (*handler)(int, siginfo_t*, void*)) {
Reset();
struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
if (sigaction(signo, &act, &old_action_) != 0) {
PrintErrmsg(errno);
return false;
}
signo_ = signo;
valid_ = true;
return true;
}
private:
void PrintErrmsg(int err) {
char errmsg[256];
if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) {
snprintf(errmsg,
sizeof(errmsg),
"%s:%d: Couldn't set errmsg for %d: %d",
__FILE__,
__LINE__,
err,
errno);
return;
}
fprintf(stderr, "%s:%d: sigaction: %s", __FILE__, __LINE__, errmsg);
}
struct sigaction old_action_;
int signo_;
bool valid_;
};
bool IsKitKat() {
char prop_buf[PROP_VALUE_MAX];
int length = __system_property_get("ro.build.version.sdk", prop_buf);
if (length <= 0) {
fprintf(stderr, "%s:%d: Couldn't get version", __FILE__, __LINE__);
// It's safer to assume this is KitKat and execute dlsym with a signal
// handler installed.
return true;
}
if (strcmp(prop_buf, "19") == 0 || strcmp(prop_buf, "20") == 0) {
return true;
}
return false;
}
class ScopedSetTID {
public:
explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); }
~ScopedSetTID() { *tid_ = -1; }
private:
pid_t* tid_;
};
sigjmp_buf dlsym_sigjmp_env;
pid_t dlsym_tid = -1;
void HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) {
if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) {
return;
}
siglongjmp(dlsym_sigjmp_env, 1);
}
} // namespace
void* Dlsym(void* handle, const char* symbol) {
if (!IsKitKat()) {
return dlsym(handle, symbol);
}
static std::mutex* signal_handler_mutex = new std::mutex();
std::lock_guard<std::mutex> lock(*signal_handler_mutex);
ScopedSetTID set_tid(&dlsym_tid);
ScopedSigactionRestore sig_restore;
if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) {
return nullptr;
}
if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) {
return nullptr;
}
return dlsym(handle, symbol);
}
#else
void* Dlsym(void* handle, const char* symbol) {
return dlsym(handle, symbol);
}
#endif // __ANDROID_API__ < 21
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,35 @@
// Copyright 2017 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.
#ifndef CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_
#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_
namespace crashpad {
namespace internal {
//! \brief Provide a wrapper for `dlsym`.
//!
//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a
//! non-existent symbol. This wrapper avoids crashing in this circumstance.
//! https://code.google.com/p/android/issues/detail?id=61799
//!
//! The parameters and return value for this function are the same as for
//! `dlsym`, but a return value for `dlerror` may not be set in the event of an
//! error.
void* Dlsym(void* handle, const char* symbol);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_

View File

@ -18,18 +18,17 @@
#include <sys/syscall.h>
#include <unistd.h>
#include "dlfcn_internal.h"
#if __ANDROID_API__ < 21
extern "C" {
int epoll_create1(int flags) {
static const auto epoll_create1_p =
reinterpret_cast<int (*)(int)>(dlsym(RTLD_DEFAULT, "epoll_create1"));
if (epoll_create1_p) {
return epoll_create1_p(flags);
}
return syscall(SYS_epoll_create1, flags);
static const auto epoll_create1_p = reinterpret_cast<int (*)(int)>(
crashpad::internal::Dlsym(RTLD_DEFAULT, "epoll_create1"));
return epoll_create1_p ? epoll_create1_p(flags)
: syscall(SYS_epoll_create1, flags);
}
} // extern "C"

View File

@ -19,6 +19,8 @@
#include <stdint.h>
#include <unistd.h>
#include "dlfcn_internal.h"
#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21
// Bionic has provided a wrapper for __mmap2() since the beginning of time. See
@ -87,8 +89,8 @@ void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) {
// Use the systems mmap64() wrapper if available. It will be available on
// Android 5.0 (“Lollipop”) and later.
using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t);
static const Mmap64Type mmap64 =
reinterpret_cast<Mmap64Type>(dlsym(RTLD_DEFAULT, "mmap64"));
static const Mmap64Type mmap64 = reinterpret_cast<Mmap64Type>(
crashpad::internal::Dlsym(RTLD_DEFAULT, "mmap64"));
if (mmap64) {
return mmap64(addr, size, prot, flags, fd, offset);
}

View File

@ -21,6 +21,8 @@
'target_name': 'crashpad_compat',
'type': 'static_library',
'sources': [
'android/dlfcn_internal.cc',
'android/dlfcn_internal.h',
'android/elf.h',
'android/linux/elf.h',
'android/linux/prctl.h',