Merge "libbacktrace: export offline unwinding failures."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e2e9577..dc45959 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -71,3 +71,6 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 6b30be8..0e38897 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -240,7 +240,10 @@
D("Calling send_connect");
apacket* cp = get_apacket();
cp->msg.command = A_CNXN;
- cp->msg.arg0 = t->get_protocol_version();
+ // Send the max supported version, but because the transport is
+ // initialized to A_VERSION_MIN, this will be compatible with every
+ // device.
+ cp->msg.arg0 = A_VERSION;
cp->msg.arg1 = t->get_max_payload();
std::string connection_str = get_connection_string();
diff --git a/adb/adb.h b/adb/adb.h
index 3651413..21e5d4b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -44,7 +44,12 @@
#define A_AUTH 0x48545541
// ADB protocol version.
-#define A_VERSION 0x01000000
+// Version revision:
+// 0x01000000: original
+// 0x01000001: skip checksum (Dec 2017)
+#define A_VERSION_MIN 0x01000000
+#define A_VERSION_SKIP_CHECKSUM 0x01000001
+#define A_VERSION 0x01000001
// Used for help/version information.
#define ADB_VERSION_MAJOR 1
@@ -53,7 +58,7 @@
std::string adb_version();
// Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 39
+#define ADB_SERVER_VERSION 40
using TransportId = uint64_t;
class atransport;
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 0c7e1f9..f9f80c0 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -262,6 +262,7 @@
env["HOSTNAME"] = GetHostName();
env["LOGNAME"] = pw->pw_name;
env["SHELL"] = pw->pw_shell;
+ env["TMPDIR"] = "/data/local/tmp";
env["USER"] = pw->pw_name;
}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 089a1ec..5cf2450 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -163,7 +163,12 @@
void send_packet(apacket* p, atransport* t) {
p->msg.magic = p->msg.command ^ 0xffffffff;
- p->msg.data_check = calculate_apacket_checksum(p);
+ // compute a checksum for connection/auth packets for compatibility reasons
+ if (t->get_protocol_version() >= A_VERSION_SKIP_CHECKSUM) {
+ p->msg.data_check = 0;
+ } else {
+ p->msg.data_check = calculate_apacket_checksum(p);
+ }
print_packet("send", p);
@@ -1089,10 +1094,6 @@
return true;
}
-bool check_data(apacket* p) {
- return calculate_apacket_checksum(p) == p->msg.data_check;
-}
-
#if ADB_HOST
std::shared_ptr<RSA> atransport::NextKey() {
if (keys_.empty()) keys_ = adb_auth_get_private_keys();
diff --git a/adb/transport.h b/adb/transport.h
index 8c101fd..86cd992 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -66,7 +66,9 @@
atransport(ConnectionState state = kCsOffline)
: id(NextTransportId()), connection_state_(state) {
transport_fde = {};
- protocol_version = A_VERSION;
+ // Initialize protocol to min version for compatibility with older versions.
+ // Version will be updated post-connect.
+ protocol_version = A_VERSION_MIN;
max_payload = MAX_PAYLOAD;
}
virtual ~atransport() {}
@@ -223,7 +225,6 @@
void unregister_usb_transport(usb_handle* usb);
bool check_header(apacket* p, atransport* t);
-bool check_data(apacket* p);
void close_usb_devices();
void close_usb_devices(std::function<bool(const atransport*)> predicate);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9cd378c..d6c84da 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -77,11 +77,6 @@
return -1;
}
- if (!check_data(p)) {
- D("bad data: terminated (data)");
- return -1;
- }
-
return 0;
}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index c3ac344..3474820 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -109,10 +109,6 @@
goto err_msg;
}
}
- if (!check_data(p)) {
- D("remote usb: check_data failed, skip it");
- goto err_msg;
- }
return 0;
err_msg:
@@ -143,11 +139,6 @@
}
}
- if (!check_data(p)) {
- LOG(ERROR) << "remote usb: check_data failed";
- return -1;
- }
-
return 0;
}
#endif
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 17a9f3a..c473e33 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -4,9 +4,12 @@
"-Wall",
"-Wextra",
"-Werror",
+ "-Wno-unused-argument",
+ "-Wno-unused-function",
"-Wno-nullability-completeness",
"-Os",
],
+ cpp_std: "gnu++17",
local_include_dirs: ["include"],
}
@@ -73,6 +76,7 @@
whole_static_libs: [
"libasync_safe",
+ "libcutils",
"libdebuggerd",
],
@@ -148,27 +152,6 @@
"libdebuggerd/utility.cpp",
],
- target: {
- android_arm: {
- srcs: ["libdebuggerd/arm/machine.cpp"],
- },
- android_arm64: {
- srcs: ["libdebuggerd/arm64/machine.cpp"],
- },
- android_mips: {
- srcs: ["libdebuggerd/mips/machine.cpp"],
- },
- android_mips64: {
- srcs: ["libdebuggerd/mips64/machine.cpp"],
- },
- android_x86: {
- srcs: ["libdebuggerd/x86/machine.cpp"],
- },
- android_x86_64: {
- srcs: ["libdebuggerd/x86_64/machine.cpp"],
- },
- },
-
local_include_dirs: ["libdebuggerd/include"],
export_include_dirs: ["libdebuggerd/include"],
@@ -193,7 +176,6 @@
"libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp",
"libdebuggerd/test/open_files_list_test.cpp",
- "libdebuggerd/test/ptrace_fake.cpp",
"libdebuggerd/test/tombstone_test.cpp",
],
@@ -218,6 +200,7 @@
static_libs: [
"libdebuggerd",
+ "libunwindstack",
],
local_include_dirs: [
@@ -264,6 +247,7 @@
"libbase",
"liblog",
"libprocinfo",
+ "libunwindstack",
],
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 827420e..a1f0211 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -18,11 +18,11 @@
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
-#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/un.h>
+#include <sys/wait.h>
#include <syscall.h>
#include <unistd.h>
@@ -47,6 +47,8 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
+#include <unwindstack/Regs.h>
+
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
#include "libdebuggerd/utility.h"
@@ -58,21 +60,9 @@
#include "util.h"
using android::base::unique_fd;
-using android::base::ReadFileToString;
using android::base::StringPrintf;
-using android::base::Trim;
-static std::string get_process_name(pid_t pid) {
- std::string result = "<unknown>";
- ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &result);
- return result;
-}
-
-static std::string get_thread_name(pid_t tid) {
- std::string result = "<unknown>";
- ReadFileToString(StringPrintf("/proc/%d/comm", tid), &result);
- return Trim(result);
-}
+using unwindstack::Regs;
static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
struct stat st;
@@ -90,8 +80,8 @@
}
// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
- if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {
+ if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {
if (errno == EPERM) {
pid_t tracer = get_tracer(tid);
if (tracer != -1) {
@@ -108,18 +98,43 @@
// Make sure that the task we attached to is actually part of the pid we're dumping.
if (!pid_contains_tid(pid_proc_fd, tid)) {
if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
- PLOG(FATAL) << "failed to detach from thread " << tid;
+ PLOG(WARNING) << "failed to detach from thread " << tid;
}
*error = StringPrintf("thread %d is not in process", tid);
return false;
}
- // Put the task into ptrace-stop state.
- if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
- PLOG(FATAL) << "failed to interrupt thread " << tid;
+ return true;
+}
+
+static bool wait_for_stop(pid_t tid, int* received_signal) {
+ while (true) {
+ int status;
+ pid_t result = waitpid(tid, &status, __WALL);
+ if (result != tid) {
+ PLOG(ERROR) << "waitpid failed on " << tid << " while detaching";
+ return false;
+ }
+
+ if (WIFSTOPPED(status)) {
+ if (status >> 16 == PTRACE_EVENT_STOP) {
+ *received_signal = 0;
+ } else {
+ *received_signal = WSTOPSIG(status);
+ }
+ return true;
+ }
+ }
+}
+
+// Interrupt a process and wait for it to be interrupted.
+static bool ptrace_interrupt(pid_t tid, int* received_signal) {
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) == 0) {
+ return wait_for_stop(tid, received_signal);
}
- return true;
+ PLOG(ERROR) << "failed to interrupt " << tid << " to detach";
+ return false;
}
static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
@@ -169,70 +184,39 @@
return true;
}
-static void signal_handler(int) {
- // We can't log easily, because the heap might be corrupt.
- // Just die and let the surrounding log context explain things.
- _exit(1);
-}
+// Globals used by the abort handler.
+static pid_t g_target_thread = -1;
+static bool g_tombstoned_connected = false;
+static unique_fd g_tombstoned_socket;
+static unique_fd g_output_fd;
-static void abort_handler(pid_t target, const bool tombstoned_connected,
- unique_fd& tombstoned_socket, unique_fd& output_fd,
- const char* abort_msg) {
- // If we abort before we get an output fd, contact tombstoned to let any
- // potential listeners know that we failed.
- if (!tombstoned_connected) {
- if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
- // We failed to connect, not much we can do.
- LOG(ERROR) << "failed to connected to tombstoned to report failure";
- _exit(1);
- }
- }
-
- dprintf(output_fd.get(), "crash_dump failed to dump process");
- if (target != 1) {
- dprintf(output_fd.get(), " %d: %s\n", target, abort_msg);
- } else {
- dprintf(output_fd.get(), ": %s\n", abort_msg);
- }
-
- _exit(1);
-}
-
-static void drop_capabilities() {
- ATRACE_CALL();
- __user_cap_header_struct capheader;
- memset(&capheader, 0, sizeof(capheader));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- __user_cap_data_struct capdata[2];
- memset(&capdata, 0, sizeof(capdata));
-
- if (capset(&capheader, &capdata[0]) == -1) {
- PLOG(FATAL) << "failed to drop capabilities";
- }
-
- if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
- PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
- }
-}
-
-int main(int argc, char** argv) {
- atrace_begin(ATRACE_TAG, "before reparent");
-
- pid_t target = getppid();
- bool tombstoned_connected = false;
- unique_fd tombstoned_socket;
- unique_fd output_fd;
-
+static void Initialize(char** argv) {
android::base::InitLogging(argv);
- android::base::SetAborter([&](const char* abort_msg) {
- abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+ android::base::SetAborter([](const char* abort_msg) {
+ // If we abort before we get an output fd, contact tombstoned to let any
+ // potential listeners know that we failed.
+ if (!g_tombstoned_connected) {
+ if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
+ kDebuggerdAnyIntercept)) {
+ // We failed to connect, not much we can do.
+ LOG(ERROR) << "failed to connected to tombstoned to report failure";
+ _exit(1);
+ }
+ }
+
+ dprintf(g_output_fd.get(), "crash_dump failed to dump process");
+ if (g_target_thread != 1) {
+ dprintf(g_output_fd.get(), " %d: %s\n", g_target_thread, abort_msg);
+ } else {
+ dprintf(g_output_fd.get(), ": %s\n", abort_msg);
+ }
+
+ _exit(1);
});
// Don't try to dump ourselves.
struct sigaction action = {};
- action.sa_handler = signal_handler;
+ action.sa_handler = SIG_DFL;
debuggerd_register_handlers(&action);
sigset_t mask;
@@ -240,216 +224,328 @@
if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
PLOG(FATAL) << "failed to set signal mask";
}
+}
+static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) {
if (argc != 4) {
- LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
+ LOG(FATAL) << "wrong number of args: " << argc << " (expected 4)";
}
- pid_t main_tid;
- pid_t pseudothread_tid;
- int dump_type;
-
- if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
- LOG(FATAL) << "invalid main tid: " << argv[1];
+ if (!android::base::ParseInt(argv[1], &g_target_thread, 1, std::numeric_limits<pid_t>::max())) {
+ LOG(FATAL) << "invalid target tid: " << argv[1];
}
- if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
+ if (!android::base::ParseInt(argv[2], pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
}
- if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+ int dump_type_int;
+ if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) {
LOG(FATAL) << "invalid requested dump type: " << argv[3];
}
+ *dump_type = static_cast<DebuggerdDumpType>(dump_type_int);
+}
- if (target == 1) {
- LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
+static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
+ std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+ std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
+ if (rc == -1) {
+ PLOG(FATAL) << "failed to read target ucontext";
+ } else if (rc != sizeof(CrashInfo)) {
+ LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+ << sizeof(CrashInfo);
}
- android::procinfo::ProcessInfo target_info;
- if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
- LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+ CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
+ if (crash_info->version != 1) {
+ LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
}
- if (main_tid != target_info.tid || target != target_info.pid) {
- LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
- << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+ *siginfo = crash_info->siginfo;
+ regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
+ *abort_address = crash_info->abort_msg_address;
+}
+
+// Wait for a process to clone and return the child's pid.
+// Note: this leaves the parent in PTRACE_EVENT_STOP.
+static pid_t wait_for_clone(pid_t pid, bool resume_child) {
+ int status;
+ pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to waitpid";
}
- // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
- std::string target_proc_path = "/proc/" + std::to_string(target);
+ if (WIFEXITED(status)) {
+ LOG(FATAL) << "traced process exited with status " << WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ LOG(FATAL) << "traced process exited with signal " << WTERMSIG(status);
+ } else if (!WIFSTOPPED(status)) {
+ LOG(FATAL) << "process didn't stop? (status = " << status << ")";
+ }
+
+ if (status >> 8 != (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
+ LOG(FATAL) << "process didn't stop due to PTRACE_O_TRACECLONE (status = " << status << ")";
+ }
+
+ pid_t child;
+ if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &child) != 0) {
+ PLOG(FATAL) << "failed to get child pid via PTRACE_GETEVENTMSG";
+ }
+
+ int stop_signal;
+ if (!wait_for_stop(child, &stop_signal)) {
+ PLOG(FATAL) << "failed to waitpid on child";
+ }
+
+ CHECK_EQ(0, stop_signal);
+
+ if (resume_child) {
+ if (ptrace(PTRACE_CONT, child, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to resume child (pid = " << child << ")";
+ }
+ }
+
+ return child;
+}
+
+static pid_t wait_for_vm_process(pid_t pseudothread_tid) {
+ // The pseudothread will double-fork, we want its grandchild.
+ pid_t intermediate = wait_for_clone(pseudothread_tid, true);
+ pid_t vm_pid = wait_for_clone(intermediate, false);
+ if (ptrace(PTRACE_DETACH, intermediate, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to detach from intermediate vm process";
+ }
+
+ return vm_pid;
+}
+
+int main(int argc, char** argv) {
+ atrace_begin(ATRACE_TAG, "before reparent");
+ pid_t target_process = getppid();
+
+ // Open /proc/`getppid()` before we daemonize.
+ std::string target_proc_path = "/proc/" + std::to_string(target_process);
int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
if (target_proc_fd == -1) {
PLOG(FATAL) << "failed to open " << target_proc_path;
}
- // Make sure our parent didn't die.
- if (getppid() != target) {
- PLOG(FATAL) << "parent died";
+ // Make sure getppid() hasn't changed.
+ if (getppid() != target_process) {
+ LOG(FATAL) << "parent died";
}
-
atrace_end(ATRACE_TAG);
// Reparent ourselves to init, so that the signal handler can waitpid on the
// original process to avoid leaving a zombie for non-fatal dumps.
+ // Move the input/output pipes off of stdout/stderr, out of paranoia.
+ unique_fd output_pipe(dup(STDOUT_FILENO));
+ unique_fd input_pipe(dup(STDIN_FILENO));
+
+ unique_fd fork_exit_read, fork_exit_write;
+ if (!Pipe(&fork_exit_read, &fork_exit_write)) {
+ PLOG(FATAL) << "failed to create pipe";
+ }
+
pid_t forkpid = fork();
if (forkpid == -1) {
PLOG(FATAL) << "fork failed";
- } else if (forkpid != 0) {
- exit(0);
+ } else if (forkpid == 0) {
+ fork_exit_read.reset();
+ } else {
+ // We need the pseudothread to live until we get around to verifying the vm pid against it.
+ // The last thing it does is block on a waitpid on us, so wait until our child tells us to die.
+ fork_exit_write.reset();
+ char buf;
+ TEMP_FAILURE_RETRY(read(fork_exit_read.get(), &buf, sizeof(buf)));
+ _exit(0);
}
ATRACE_NAME("after reparent");
+ pid_t pseudothread_tid;
+ DebuggerdDumpType dump_type;
+ uintptr_t abort_address = 0;
+
+ Initialize(argv);
+ ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
// Die if we take too long.
//
// Note: processes with many threads and minidebug-info can take a bit to
// unwind, do not make this too small. b/62828735
- alarm(5);
+ alarm(30);
- std::string attach_error;
-
- std::map<pid_t, std::string> threads;
-
- {
- ATRACE_NAME("ptrace");
- // Seize the main thread.
- if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
- LOG(FATAL) << attach_error;
- }
-
- // Seize the siblings.
- {
- std::set<pid_t> siblings;
- if (!android::procinfo::GetProcessTids(target, &siblings)) {
- PLOG(FATAL) << "failed to get process siblings";
- }
-
- // but not the already attached main thread.
- siblings.erase(main_tid);
- // or the handler pseudothread.
- siblings.erase(pseudothread_tid);
-
- for (pid_t sibling_tid : siblings) {
- if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
- LOG(WARNING) << attach_error;
- } else {
- threads.emplace(sibling_tid, get_thread_name(sibling_tid));
- }
- }
- }
- }
-
- // Collect the backtrace map, open files, and process/thread names, while we still have caps.
- std::unique_ptr<BacktraceMap> backtrace_map;
- {
- ATRACE_NAME("backtrace map");
- backtrace_map.reset(BacktraceMap::Create(main_tid));
- if (!backtrace_map) {
- LOG(FATAL) << "failed to create backtrace map";
- }
- }
+ // Get the process name (aka cmdline).
+ std::string process_name = get_process_name(g_target_thread);
// Collect the list of open files.
OpenFilesList open_files;
{
ATRACE_NAME("open files");
- populate_open_files_list(target, &open_files);
+ populate_open_files_list(g_target_thread, &open_files);
}
- std::string process_name = get_process_name(main_tid);
- threads.emplace(main_tid, get_thread_name(main_tid));
+ // In order to reduce the duration that we pause the process for, we ptrace
+ // the threads, fetch their registers and associated information, and then
+ // fork a separate process as a snapshot of the process's address space.
+ std::set<pid_t> threads;
+ if (!android::procinfo::GetProcessTids(g_target_thread, &threads)) {
+ PLOG(FATAL) << "failed to get process threads";
+ }
- // Drop our capabilities now that we've attached to the threads we care about.
+ std::map<pid_t, ThreadInfo> thread_info;
+ siginfo_t siginfo;
+ std::string error;
+
+ {
+ ATRACE_NAME("ptrace");
+ for (pid_t thread : threads) {
+ // Trace the pseudothread separately, so we can use different options.
+ if (thread == pseudothread_tid) {
+ continue;
+ }
+
+ if (!ptrace_seize_thread(target_proc_fd, thread, &error)) {
+ bool fatal = thread == g_target_thread;
+ LOG(fatal ? FATAL : WARNING) << error;
+ }
+
+ ThreadInfo info;
+ info.pid = target_process;
+ info.tid = thread;
+ info.process_name = process_name;
+ info.thread_name = get_thread_name(thread);
+
+ if (!ptrace_interrupt(thread, &info.signo)) {
+ PLOG(WARNING) << "failed to ptrace interrupt thread " << thread;
+ ptrace(PTRACE_DETACH, thread, 0, 0);
+ continue;
+ }
+
+ if (thread == g_target_thread) {
+ // Read the thread's registers along with the rest of the crash info out of the pipe.
+ ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address);
+ info.siginfo = &siginfo;
+ info.signo = info.siginfo->si_signo;
+ } else {
+ info.registers.reset(Regs::RemoteGet(thread));
+ if (!info.registers) {
+ PLOG(WARNING) << "failed to fetch registers for thread " << thread;
+ ptrace(PTRACE_DETACH, thread, 0, 0);
+ continue;
+ }
+ }
+
+ thread_info[thread] = std::move(info);
+ }
+ }
+
+ // Trace the pseudothread with PTRACE_O_TRACECLONE and tell it to fork.
+ if (!ptrace_seize_thread(target_proc_fd, pseudothread_tid, &error, PTRACE_O_TRACECLONE)) {
+ LOG(FATAL) << "failed to seize pseudothread: " << error;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(output_pipe.get(), "\1", 1)) != 1) {
+ PLOG(FATAL) << "failed to write to pseudothread";
+ }
+
+ pid_t vm_pid = wait_for_vm_process(pseudothread_tid);
+ if (ptrace(PTRACE_DETACH, pseudothread_tid, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to detach from pseudothread";
+ }
+
+ // The pseudothread can die now.
+ fork_exit_write.reset();
+
+ // Defer the message until later, for readability.
+ bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+ if (siginfo.si_signo == DEBUGGER_SIGNAL) {
+ wait_for_gdb = false;
+ }
+
+ // Detach from all of our attached threads before resuming.
+ for (const auto& [tid, thread] : thread_info) {
+ int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo;
+ if (wait_for_gdb) {
+ resume_signal = 0;
+ if (tgkill(target_process, tid, SIGSTOP) != 0) {
+ PLOG(WARNING) << "failed to send SIGSTOP to " << tid;
+ }
+ }
+
+ LOG(DEBUG) << "detaching from thread " << tid;
+ if (ptrace(PTRACE_DETACH, tid, 0, resume_signal) != 0) {
+ PLOG(ERROR) << "failed to detach from thread " << tid;
+ }
+ }
+
+ // Drop our capabilities now that we've fetched all of the information we need.
drop_capabilities();
{
ATRACE_NAME("tombstoned_connect");
- const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
- LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
- tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+ LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type;
+ g_tombstoned_connected =
+ tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type);
}
- // Write a '\1' to stdout to tell the crashing process to resume.
- // It also restores the value of PR_SET_DUMPABLE at this point.
- if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
- PLOG(ERROR) << "failed to communicate to target process";
- }
-
- if (tombstoned_connected) {
- if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
- PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+ if (g_tombstoned_connected) {
+ if (TEMP_FAILURE_RETRY(dup2(g_output_fd.get(), STDOUT_FILENO)) == -1) {
+ PLOG(ERROR) << "failed to dup2 output fd (" << g_output_fd.get() << ") to STDOUT_FILENO";
}
} else {
unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
- output_fd = std::move(devnull);
+ g_output_fd = std::move(devnull);
}
- LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
-
- // At this point, the thread that made the request has been attached and is
- // in ptrace-stopped state. After resumption, the triggering signal that has
- // been queued will be delivered.
- if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
- PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
- exit(1);
- }
-
- siginfo_t siginfo = {};
- {
- ATRACE_NAME("wait_for_signal");
- if (!wait_for_signal(main_tid, &siginfo)) {
- printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
- exit(1);
- }
- }
+ LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
+ << ")";
int signo = siginfo.si_signo;
bool fatal_signal = signo != DEBUGGER_SIGNAL;
bool backtrace = false;
- uintptr_t abort_address = 0;
- // si_value can represent three things:
+ // si_value is special when used with DEBUGGER_SIGNAL.
// 0: dump tombstone
// 1: dump backtrace
- // everything else: abort message address (implies dump tombstone)
- if (siginfo.si_value.sival_int == 1) {
- backtrace = true;
- } else if (siginfo.si_value.sival_ptr != nullptr) {
- abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+ if (!fatal_signal) {
+ int si_val = siginfo.si_value.sival_int;
+ if (si_val == 0) {
+ backtrace = false;
+ } else if (si_val == 1) {
+ backtrace = true;
+ } else {
+ LOG(WARNING) << "unknown si_value value " << si_val;
+ }
}
// TODO: Use seccomp to lock ourselves down.
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(vm_pid, false));
+ if (!map) {
+ LOG(FATAL) << "failed to create backtrace map";
+ }
+
+ std::shared_ptr<unwindstack::Memory> process_memory = map->GetProcessMemory();
+ if (!process_memory) {
+ LOG(FATAL) << "failed to get unwindstack::Memory handle";
+ }
std::string amfd_data;
if (backtrace) {
ATRACE_NAME("dump_backtrace");
- dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
+ dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
} else {
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
- process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+ engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
+ g_target_thread, abort_address, &open_files, &amfd_data);
}
- // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
- // group-stop state, which is true as long as no stopping signals are sent.
-
- bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
- if (!fatal_signal || siginfo.si_code == SI_USER) {
- // Don't wait_for_gdb when the process didn't actually crash.
- wait_for_gdb = false;
- }
-
- // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
- // get it in a state where it can receive signals, and then send the relevant
- // signal.
- if (wait_for_gdb || fatal_signal) {
- if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
- PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
- }
-
- if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
- PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+ if (fatal_signal) {
+ // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+ if (thread_info[target_process].thread_name != "system_server") {
+ activity_manager_notify(target_process, signo, amfd_data);
}
}
@@ -463,19 +559,12 @@
"* gdbclient.py -p %d\n"
"*\n"
"***********************************************************",
- target, target);
- }
-
- if (fatal_signal) {
- // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
- if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
- activity_manager_notify(target, signo, amfd_data);
- }
+ target_process, target_process);
}
// Close stdout before we notify tombstoned of completion.
close(STDOUT_FILENO);
- if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
+ if (g_tombstoned_connected && !tombstoned_notify_completion(g_tombstoned_socket.get())) {
LOG(ERROR) << "failed to notify tombstoned of completion";
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 8d0c98b..0d17a3b 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -245,6 +245,8 @@
int status;
pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
if (pid != crasher_pid) {
+ printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+ sleep(100);
FAIL() << "failed to wait for crasher: " << strerror(errno);
}
@@ -341,13 +343,12 @@
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
- abort();
+ while (true) {
+ sleep(1);
+ }
});
StartIntercept(&output_fd);
-
- // Wait for a bit, or we might end up killing the process before the signal
- // handler even gets a chance to be registered.
- std::this_thread::sleep_for(100ms);
+ FinishCrasher();
ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
AssertDeath(SIGSEGV);
@@ -439,19 +440,6 @@
AssertDeath(SIGABRT);
}
-// wait_for_gdb shouldn't trigger on manually sent signals.
-TEST_F(CrasherTest, wait_for_gdb_signal) {
- if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
- FAIL() << "failed to enable wait_for_gdb";
- }
-
- StartProcess([]() {
- abort();
- });
- ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
- AssertDeath(SIGSEGV);
-}
-
TEST_F(CrasherTest, backtrace) {
std::string result;
int intercept_result;
@@ -596,15 +584,13 @@
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
- while (true) {
- }
+ raise(SIGABRT);
});
StartIntercept(&output_fd);
- FinishCrasher();
ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
- ASSERT_EQ(0, kill(crasher_pid, SIGABRT));
+ FinishCrasher();
int status;
ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
@@ -622,6 +608,10 @@
regex += R"( \(.+debuggerd_test)";
ASSERT_MATCH(result, regex.c_str());
+ ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+ ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
AssertDeath(SIGABRT);
}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 06d4a9b..5fddddc 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -36,10 +36,14 @@
#include <unistd.h>
#include <atomic>
+#include <memory>
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
#include "debuggerd/handler.h"
#include "tombstoned/tombstoned.h"
@@ -49,6 +53,7 @@
#include "libdebuggerd/tombstone.h"
using android::base::unique_fd;
+using unwindstack::Regs;
extern "C" void __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator();
@@ -61,7 +66,19 @@
// exhaustion.
static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
__linker_enable_fallback_allocator();
- dump_backtrace_ucontext(output_fd, ucontext);
+ {
+ std::unique_ptr<Regs> regs;
+
+ ThreadInfo thread;
+ thread.pid = getpid();
+ thread.tid = gettid();
+ thread.thread_name = get_thread_name(gettid());
+ thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+ // TODO: Create this once and store it in a global?
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+ dump_backtrace_thread(output_fd, map.get(), thread);
+ }
__linker_disable_fallback_allocator();
}
@@ -162,41 +179,41 @@
// Send a signal to all of our siblings, asking them to dump their stack.
iterate_siblings(
- [](pid_t tid, int output_fd) {
- // Use a pipe, to be able to detect situations where the thread gracefully exits before
- // receiving our signal.
- unique_fd pipe_read, pipe_write;
- if (!Pipe(&pipe_read, &pipe_write)) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
- strerror(errno));
- return false;
- }
+ [](pid_t tid, int output_fd) {
+ // Use a pipe, to be able to detect situations where the thread gracefully exits before
+ // receiving our signal.
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+ strerror(errno));
+ return false;
+ }
- trace_output_fd.store(pipe_write.get());
+ trace_output_fd.store(pipe_write.get());
- siginfo_t siginfo = {};
- siginfo.si_code = SI_QUEUE;
- siginfo.si_value.sival_int = ~0;
- siginfo.si_pid = getpid();
- siginfo.si_uid = getuid();
+ siginfo_t siginfo = {};
+ siginfo.si_code = SI_QUEUE;
+ siginfo.si_value.sival_int = ~0;
+ siginfo.si_pid = getpid();
+ siginfo.si_uid = getuid();
- if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
- tid, strerror(errno));
- return false;
- }
+ if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+ tid, strerror(errno));
+ return false;
+ }
- bool success = forward_output(pipe_read.get(), output_fd);
- if (success) {
- // The signaled thread has closed trace_output_fd already.
- (void)pipe_write.release();
- } else {
- trace_output_fd.store(-1);
- }
+ bool success = forward_output(pipe_read.get(), output_fd);
+ if (success) {
+ // The signaled thread has closed trace_output_fd already.
+ (void)pipe_write.release();
+ } else {
+ trace_output_fd.store(-1);
+ }
- return true;
- },
- output_fd.get());
+ return true;
+ },
+ output_fd.get());
dump_backtrace_footer(output_fd.get());
tombstoned_notify_completion(tombstone_socket.get());
@@ -206,7 +223,8 @@
}
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
- // Only allow one thread to handle a crash.
+ // Only allow one thread to handle a crash at a time (this can happen multiple times without
+ // exit, since tombstones can be requested without a real crash happening.)
static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_lock(&crash_mutex);
if (ret != 0) {
@@ -221,11 +239,13 @@
if (tombstoned_connected) {
tombstoned_notify_completion(tombstone_socket.get());
}
+
+ pthread_mutex_unlock(&crash_mutex);
}
extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
void* abort_message) {
- if (info->si_signo == DEBUGGER_SIGNAL) {
+ if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
return trace_handler(info, ucontext);
} else {
return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index bd202ff..02bc4b8 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -44,15 +44,21 @@
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/syscall.h>
+#include <sys/uio.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
+#include <cutils/properties.h>
+
+#include <libdebuggerd/utility.h>
#include "dump_type.h"
+#include "protocol.h"
+using android::base::Pipe;
using android::base::unique_fd;
// see man(2) prctl, specifically the section about PR_GET_NAME
@@ -114,7 +120,7 @@
va_list args;
va_start(args, fmt);
- char buf[4096];
+ char buf[256];
async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
fatal("%s: %s", buf, strerror(err));
}
@@ -147,7 +153,7 @@
* mutex is being held, so we don't want to use any libc functions that
* could allocate memory or hold a lock.
*/
-static void log_signal_summary(int signum, const siginfo_t* info) {
+static void log_signal_summary(const siginfo_t* info) {
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
strcpy(thread_name, "<name unknown>");
@@ -157,57 +163,19 @@
thread_name[MAX_TASK_NAME_LEN] = 0;
}
- if (signum == DEBUGGER_SIGNAL) {
+ if (info->si_signo == DEBUGGER_SIGNAL) {
async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
thread_name);
return;
}
- const char* signal_name = "???";
- bool has_address = false;
- switch (signum) {
- case SIGABRT:
- signal_name = "SIGABRT";
- break;
- case SIGBUS:
- signal_name = "SIGBUS";
- has_address = true;
- break;
- case SIGFPE:
- signal_name = "SIGFPE";
- has_address = true;
- break;
- case SIGILL:
- signal_name = "SIGILL";
- has_address = true;
- break;
- case SIGSEGV:
- signal_name = "SIGSEGV";
- has_address = true;
- break;
-#if defined(SIGSTKFLT)
- case SIGSTKFLT:
- signal_name = "SIGSTKFLT";
- break;
-#endif
- case SIGSYS:
- signal_name = "SIGSYS";
- break;
- case SIGTRAP:
- signal_name = "SIGTRAP";
- break;
- }
+ const char* signal_name = get_signame(info->si_signo);
+ bool has_address = signal_has_si_addr(info->si_signo, info->si_code);
- // "info" will be null if the siginfo_t information was not available.
- // Many signals don't have an address or a code.
- char code_desc[32]; // ", code -6"
- char addr_desc[32]; // ", fault addr 0x1234"
- addr_desc[0] = code_desc[0] = 0;
- if (info != nullptr) {
- async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
- if (has_address) {
- async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
- }
+ // Many signals don't have an address.
+ char addr_desc[32] = ""; // ", fault addr 0x1234"
+ if (has_address) {
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
char main_thread_name[MAX_TASK_NAME_LEN + 1];
@@ -216,8 +184,9 @@
}
async_safe_format_log(
- ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
- signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
+ ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s), code %d (%s)%s in tid %d (%s), pid %d (%s)",
+ info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code),
+ addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
}
/*
@@ -268,12 +237,44 @@
}
}
+static pid_t __fork() {
+ return clone(nullptr, nullptr, 0, nullptr);
+}
+
+// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation.
+// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure.
+static void create_vm_process() {
+ pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr);
+ if (first == -1) {
+ fatal_errno("failed to clone vm process");
+ } else if (first == 0) {
+ drop_capabilities();
+
+ if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) {
+ _exit(errno);
+ }
+
+ // Exit immediately on both sides of the fork.
+ // crash_dump is ptracing us, so it'll get to do whatever it wants in between.
+ _exit(0);
+ }
+
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) {
+ fatal_errno("failed to waitpid in double fork");
+ } else if (!WIFEXITED(status)) {
+ fatal("intermediate process didn't exit cleanly in double fork (status = %d)", status);
+ } else if (WEXITSTATUS(status)) {
+ fatal("second clone failed: %s", strerror(WEXITSTATUS(status)));
+ }
+}
+
struct debugger_thread_info {
- bool crash_dump_started;
pid_t crashing_tid;
pid_t pseudothread_tid;
- int signal_number;
- siginfo_t* info;
+ siginfo_t* siginfo;
+ void* ucontext;
+ uintptr_t abort_msg;
};
// Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -284,7 +285,8 @@
static void* pseudothread_stack;
static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
- if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+ if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL &&
+ thread_info->siginfo->si_value.sival_int) {
return kDebuggerdNativeBacktrace;
}
@@ -299,25 +301,58 @@
}
int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+ if (devnull == -1) {
+ fatal_errno("failed to open /dev/null");
+ } else if (devnull != 0) {
+ fatal_errno("expected /dev/null fd to be 0, actually %d", devnull);
+ }
// devnull will be 0.
- TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
- TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+ TEMP_FAILURE_RETRY(dup2(devnull, 1));
+ TEMP_FAILURE_RETRY(dup2(devnull, 2));
- unique_fd pipe_read, pipe_write;
- if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+ unique_fd input_read, input_write;
+ unique_fd output_read, output_write;
+ if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) {
fatal_errno("failed to create pipe");
}
+ // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
+ uint32_t version = 1;
+ constexpr size_t expected =
+ sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t);
+
+ errno = 0;
+ if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
+ fatal_errno("failed to set pipe bufer size");
+ }
+
+ struct iovec iovs[4] = {
+ {.iov_base = &version, .iov_len = sizeof(version)},
+ {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
+ {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
+ {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+ };
+
+ ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+ if (rc == -1) {
+ fatal_errno("failed to write crash info");
+ } else if (rc != expected) {
+ fatal("failed to write crash info, wrote %zd bytes, expected %zd", rc, expected);
+ }
+
// Don't use fork(2) to avoid calling pthread_atfork handlers.
- int forkpid = clone(nullptr, nullptr, 0, nullptr);
- if (forkpid == -1) {
+ pid_t crash_dump_pid = __fork();
+ if (crash_dump_pid == -1) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
"failed to fork in debuggerd signal handler: %s", strerror(errno));
- } else if (forkpid == 0) {
- TEMP_FAILURE_RETRY(dup2(pipe_write.get(), STDOUT_FILENO));
- pipe_write.reset();
- pipe_read.reset();
+ } else if (crash_dump_pid == 0) {
+ TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO));
+ TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO));
+ input_read.reset();
+ input_write.reset();
+ output_read.reset();
+ output_write.reset();
raise_caps();
@@ -332,45 +367,49 @@
execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
nullptr, nullptr);
-
fatal_errno("exec failed");
- } else {
- pipe_write.reset();
- char buf[4];
- ssize_t rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), &buf, sizeof(buf)));
- if (rc == -1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
- strerror(errno));
- } else if (rc == 0) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
- } else if (rc != 1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc",
- "read of IPC pipe returned unexpected value: %zd", rc);
- } else {
- if (buf[0] != '\1') {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
- } else {
- thread_info->crash_dump_started = true;
- }
- }
- pipe_read.reset();
-
- // Don't leave a zombie child.
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
- strerror(errno));
- } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
- thread_info->crash_dump_started = false;
- }
}
- syscall(__NR_exit, 0);
+ input_write.reset();
+ output_read.reset();
+
+ // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell
+ // us to fork off a process to read memory from.
+ char buf[4];
+ rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
+ if (rc == -1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+ return 1;
+ } else if (rc == 0) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+ return 1;
+ } else if (rc != 1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "read of IPC pipe returned unexpected value: %zd", rc);
+ return 1;
+ } else if (buf[0] != '\1') {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+ return 1;
+ }
+
+ // crash_dump is ptracing us, fork off a copy of our address space for it to use.
+ create_vm_process();
+
+ input_read.reset();
+ input_write.reset();
+
+ // Don't leave a zombie child.
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+ strerror(errno));
+ } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+ }
return 0;
}
-static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+static void resend_signal(siginfo_t* info) {
// Signals can either be fatal or nonfatal.
// For fatal signals, crash_dump will send us the signal we crashed with
// before resuming us, so that processes using waitpid on us will see that we
@@ -379,16 +418,6 @@
// to deregister our signal handler for that signal before continuing.
if (info->si_signo != DEBUGGER_SIGNAL) {
signal(info->si_signo, SIG_DFL);
- }
-
- // We need to return from our signal handler so that crash_dump can see the
- // signal via ptrace and dump the thread that crashed. However, returning
- // does not guarantee that the signal will be thrown again, even for SIGSEGV
- // and friends, since the signal could have been sent manually. We blocked
- // all signals when registering the handler, so resending the signal (using
- // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
- // when our signal handler returns.
- if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
if (rc != 0) {
fatal_errno("failed to resend signal during crash");
@@ -425,7 +454,7 @@
}
void* abort_message = nullptr;
- if (g_callbacks.get_abort_message) {
+ if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
abort_message = g_callbacks.get_abort_message();
}
@@ -439,7 +468,7 @@
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
// ANR trace.
debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
- resend_signal(info, false);
+ resend_signal(info);
return;
}
@@ -450,20 +479,14 @@
return;
}
- log_signal_summary(signal_number, info);
-
- // If this was a fatal crash, populate si_value with the abort message address if possible.
- // Note that applications can set an abort message without aborting.
- if (abort_message && signal_number != DEBUGGER_SIGNAL) {
- info->si_value.sival_ptr = abort_message;
- }
+ log_signal_summary(info);
debugger_thread_info thread_info = {
- .crash_dump_started = false,
- .pseudothread_tid = -1,
- .crashing_tid = __gettid(),
- .signal_number = signal_number,
- .info = info
+ .pseudothread_tid = -1,
+ .crashing_tid = __gettid(),
+ .siginfo = info,
+ .ucontext = context,
+ .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
@@ -472,7 +495,8 @@
fatal_errno("failed to set dumpable");
}
- // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+ // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor
+ // exhaustion.
pid_t child_pid =
clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
@@ -484,7 +508,7 @@
// Wait for the child to start...
futex_wait(&thread_info.pseudothread_tid, -1);
- // and then wait for it to finish.
+ // and then wait for it to terminate.
futex_wait(&thread_info.pseudothread_tid, child_pid);
// Restore PR_SET_DUMPABLE to its original value.
@@ -492,21 +516,13 @@
fatal_errno("failed to restore dumpable");
}
- // Signals can either be fatal or nonfatal.
- // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
- // crashed with, so that processes using waitpid on us will see that we
- // exited with the correct exit status (e.g. so that sh will report
- // "Segmentation fault" instead of "Killed"). For this to work, we need
- // to deregister our signal handler for that signal before continuing.
- if (signal_number != DEBUGGER_SIGNAL) {
- signal(signal_number, SIG_DFL);
- }
-
- resend_signal(info, thread_info.crash_dump_started);
if (info->si_signo == DEBUGGER_SIGNAL) {
// If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
+ } else {
+ // Resend the signal, so that either gdb or the parent's waitpid sees it.
+ resend_signal(info);
}
}
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
deleted file mode 100644
index bfb5ea4..0000000
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *
- * Copyright 2006, The Android Open Source Project
- *
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- pt_regs regs;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
-
- for (int reg = 0; reg < 14; reg++) {
- dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]);
- }
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
-
- if (regs.ARM_pc != regs.ARM_lr) {
- dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
- }
-}
-
-#define DUMP_GP_REGISTERS(log, reg_prefix) \
- _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n", \
- static_cast<uint32_t>(reg_prefix##r0), static_cast<uint32_t>(reg_prefix##r1), \
- static_cast<uint32_t>(reg_prefix##r2), static_cast<uint32_t>(reg_prefix##r3)); \
- _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n", \
- static_cast<uint32_t>(reg_prefix##r4), static_cast<uint32_t>(reg_prefix##r5), \
- static_cast<uint32_t>(reg_prefix##r6), static_cast<uint32_t>(reg_prefix##r7)); \
- _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n", \
- static_cast<uint32_t>(reg_prefix##r8), static_cast<uint32_t>(reg_prefix##r9), \
- static_cast<uint32_t>(reg_prefix##r10), static_cast<uint32_t>(reg_prefix##fp)); \
- _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", \
- static_cast<uint32_t>(reg_prefix##ip), static_cast<uint32_t>(reg_prefix##sp), \
- static_cast<uint32_t>(reg_prefix##lr), static_cast<uint32_t>(reg_prefix##pc), \
- static_cast<uint32_t>(reg_prefix##cpsr))
-
-void dump_registers(log_t* log, pid_t tid) {
- pt_regs r;
- if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- DUMP_GP_REGISTERS(log, r.ARM_);
-
- user_vfp vfp_regs;
- if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
- ALOGE("cannot get FP registers: %s\n", strerror(errno));
- return;
- }
-
- for (size_t i = 0; i < 32; i += 2) {
- _LOG(log, logtype::FP_REGISTERS, " d%-2d %016llx d%-2d %016llx\n",
- i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
- }
- _LOG(log, logtype::FP_REGISTERS, " scr %08lx\n", vfp_regs.fpscr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* uc) {
- DUMP_GP_REGISTERS(log, uc->uc_mcontext.arm_);
-}
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
deleted file mode 100644
index ad1c951..0000000
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
- *
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <elf.h>
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/uio.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- struct user_pt_regs regs;
- struct iovec io;
- io.iov_base = ®s;
- io.iov_len = sizeof(regs);
-
- if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
- ALOGE("ptrace failed to get registers: %s", strerror(errno));
- return;
- }
-
- for (int reg = 0; reg < 31; reg++) {
- dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
- }
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
-
- if (regs.pc != regs.sp) {
- dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
- }
-}
-
-#define DUMP_GP_REGISTERS(log) \
- for (int i = 0; i < 28; i += 4) { \
- const char* fmt = " x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n"; \
- _LOG(log, logtype::REGISTERS, fmt, i, r.regs[i], i + 1, r.regs[i + 1], i + 2, r.regs[i + 2], \
- i + 3, r.regs[i + 3]); \
- } \
- _LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n", r.regs[28], \
- r.regs[29], r.regs[30]); \
- _LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n", r.sp, r.pc, \
- r.pstate)
-
-void dump_registers(log_t* log, pid_t tid) {
- struct user_pt_regs r;
- struct iovec io;
- io.iov_base = &r;
- io.iov_len = sizeof(r);
-
- if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
- ALOGE("ptrace error: %s\n", strerror(errno));
- return;
- }
-
- DUMP_GP_REGISTERS(log);
-
- struct user_fpsimd_state f;
- io.iov_base = &f;
- io.iov_len = sizeof(f);
-
- if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
- ALOGE("ptrace error: %s\n", strerror(errno));
- return;
- }
-
- for (int i = 0; i < 32; i += 2) {
- _LOG(log, logtype::FP_REGISTERS,
- " v%-2d %016" PRIx64 "%016" PRIx64 " v%-2d %016" PRIx64 "%016" PRIx64 "\n",
- i,
- static_cast<uint64_t>(f.vregs[i] >> 64),
- static_cast<uint64_t>(f.vregs[i]),
- i+1,
- static_cast<uint64_t>(f.vregs[i+1] >> 64),
- static_cast<uint64_t>(f.vregs[i+1]));
- }
- _LOG(log, logtype::FP_REGISTERS, " fpsr %08x fpcr %08x\n", f.fpsr, f.fpcr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* ucontext) {
- const mcontext_t& r = ucontext->uc_mcontext;
- DUMP_GP_REGISTERS(log);
-}
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f616e1b..f0a01f4 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -30,12 +30,15 @@
#include <time.h>
#include <unistd.h>
+#include <map>
#include <memory>
#include <string>
+#include <android-base/unique_fd.h>
#include <backtrace/Backtrace.h>
#include <log/log.h>
+#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
@@ -56,62 +59,46 @@
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
-static void log_thread_name(log_t* log, pid_t tid, const char* thread_name) {
- _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread_name, tid);
-}
-
-static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
- const std::string& thread_name) {
- log_thread_name(log, tid, thread_name.c_str());
-
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
- if (backtrace->Unwind(0)) {
- dump_backtrace_to_log(backtrace.get(), log, " ");
- } else {
- ALOGE("Unwind failed: tid = %d: %s", tid,
- backtrace->GetErrorString(backtrace->GetError()).c_str());
- }
-}
-
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, std::string* amfd_data) {
- log_t log;
- log.tfd = fd;
- log.amfd_data = amfd_data;
-
- dump_process_header(&log, pid, process_name.c_str());
- dump_thread(&log, map, pid, tid, threads.find(tid)->second.c_str());
-
- for (const auto& it : threads) {
- pid_t thread_tid = it.first;
- const std::string& thread_name = it.second;
- if (thread_tid != tid) {
- dump_thread(&log, map, pid, thread_tid, thread_name.c_str());
- }
- }
-
- dump_process_footer(&log, pid);
-}
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext) {
- pid_t pid = getpid();
- pid_t tid = gettid();
-
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
log_t log;
log.tfd = output_fd;
log.amfd_data = nullptr;
- char thread_name[16];
- read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
- log_thread_name(&log, tid, thread_name);
+ _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
- if (backtrace->Unwind(0, ucontext)) {
- dump_backtrace_to_log(backtrace.get(), &log, " ");
- } else {
- ALOGE("Unwind failed: tid = %d: %s", tid,
- backtrace->GetErrorString(backtrace->GetError()).c_str());
+ std::vector<backtrace_frame_data_t> frames;
+ if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+ return;
}
+
+ for (auto& frame : frames) {
+ _LOG(&log, logtype::BACKTRACE, " %s\n", Backtrace::FormatFrameData(&frame).c_str());
+ }
+}
+
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+ const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
+ log_t log;
+ log.tfd = output_fd.get();
+ log.amfd_data = nullptr;
+
+ auto target = thread_info.find(target_thread);
+ if (target == thread_info.end()) {
+ ALOGE("failed to find target thread in thread info");
+ return;
+ }
+
+ dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
+
+ dump_backtrace_thread(output_fd.get(), map, target->second);
+ for (const auto& [tid, info] : thread_info) {
+ if (tid != target_thread) {
+ dump_backtrace_thread(output_fd.get(), map, info);
+ }
+ }
+
+ dump_process_footer(&log, target->second.pid);
}
void dump_backtrace_header(int output_fd) {
@@ -131,9 +118,3 @@
dump_process_footer(&log, getpid());
}
-
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
- }
-}
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index a35102f..d7afc0b 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -26,28 +26,28 @@
#include <string>
#include <android-base/stringprintf.h>
-#include <backtrace/Backtrace.h>
#include <log/log.h>
+#include <unwindstack/Memory.h>
#define NOTE_ALIGN(size) (((size) + 3) & ~3)
template <typename HdrType, typename PhdrType, typename NhdrType>
-static bool get_build_id(
- Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident,
+ std::string* build_id) {
HdrType hdr;
memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
// First read the rest of the header.
- if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
- sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+ if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+ sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
return false;
}
for (size_t i = 0; i < hdr.e_phnum; i++) {
PhdrType phdr;
- if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
- reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+ if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+ reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
return false;
}
// Looking for the .note.gnu.build-id note.
@@ -56,7 +56,7 @@
uintptr_t addr = base_addr + phdr.p_offset;
while (hdr_size >= sizeof(NhdrType)) {
NhdrType nhdr;
- if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+ if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
return false;
}
addr += sizeof(nhdr);
@@ -69,7 +69,7 @@
nhdr.n_descsz);
return false;
}
- if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+ if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
return false;
}
@@ -95,10 +95,10 @@
return false;
}
-bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) {
// Read and verify the elf magic number first.
uint8_t e_ident[EI_NIDENT];
- if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+ if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
return false;
}
@@ -107,14 +107,14 @@
}
// Read the rest of EI_NIDENT.
- if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+ if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
return false;
}
if (e_ident[EI_CLASS] == ELFCLASS32) {
- return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+ return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(memory, addr, e_ident, build_id);
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
- return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+ return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
}
return false;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index fe738f1..119e59b 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -23,21 +23,20 @@
#include <map>
#include <string>
+#include <android-base/unique_fd.h>
+
+#include "types.h"
#include "utility.h"
-class Backtrace;
class BacktraceMap;
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, std::string* amfd_data);
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+ const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
-/* Dumps the backtrace in the backtrace data structure to the log. */
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext);
void dump_backtrace_header(int output_fd);
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread);
void dump_backtrace_footer(int output_fd);
#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
index 11d0a43..5d0d924 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
@@ -20,8 +20,10 @@
#include <stdint.h>
#include <string>
-class Backtrace;
+namespace unwindstack {
+class Memory;
+}
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
deleted file mode 100644
index 5e56682..0000000
--- a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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 _DEBUGGERD_MACHINE_H
-#define _DEBUGGERD_MACHINE_H
-
-#include <sys/types.h>
-
-#include <backtrace/Backtrace.h>
-
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace);
-void dump_registers(log_t* log, pid_t tid);
-void dump_registers(log_t* log, const ucontext_t* uc);
-
-#endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
index b37228d..4727ca4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -31,6 +31,6 @@
void populate_open_files_list(pid_t pid, OpenFilesList* list);
/* Dumps the open files list to the log. */
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix);
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);
#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 79743b6..198c48b 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -24,7 +24,10 @@
#include <map>
#include <string>
+#include <android-base/unique_fd.h>
+
#include "open_files_list.h"
+#include "types.h"
class BacktraceMap;
@@ -43,11 +46,10 @@
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
-// Compatibility shim.
-__attribute__((__unused__))
-static void engrave_tombstone_ucontext(int tombstone_fd, pid_t, pid_t, uintptr_t abort_msg_address,
- siginfo_t* siginfo, ucontext_t* ucontext) {
- engrave_tombstone_ucontext(tombstone_fd, abort_msg_address, siginfo, ucontext);
-}
+void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map,
+ unwindstack::Memory* process_memory,
+ const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
+ uintptr_t abort_msg_address, OpenFilesList* open_files,
+ std::string* amfd_data);
-#endif // _DEBUGGERD_TOMBSTONE_H
+#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
similarity index 62%
rename from debuggerd/libdebuggerd/test/ptrace_fake.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/types.h
index fdbb663..70583af 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -1,5 +1,7 @@
+#pragma once
+
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +16,19 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H
-#define _DEBUGGERD_TEST_PTRACE_FAKE_H
+#include <memory>
+#include <string>
-#include <signal.h>
+#include <unwindstack/Regs.h>
-void ptrace_set_fake_getsiginfo(const siginfo_t&);
+struct ThreadInfo {
+ std::unique_ptr<unwindstack::Regs> registers;
+ pid_t tid;
+ std::string thread_name;
-#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
+ pid_t pid;
+ std::string process_name;
+
+ int signo = 0;
+ siginfo_t* siginfo = nullptr;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index f481b78..c5abfe2 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -27,21 +27,24 @@
#include <android-base/macros.h>
#include <backtrace/Backtrace.h>
-struct log_t{
- // Tombstone file descriptor.
- int tfd;
- // Data to be sent to the Activity Manager.
- std::string* amfd_data;
- // The tid of the thread that crashed.
- pid_t crashed_tid;
- // The tid of the thread we are currently working with.
- pid_t current_tid;
- // logd daemon crash, can block asking for logcat data, allow suppression.
- bool should_retrieve_logcat;
+struct log_t {
+ // Tombstone file descriptor.
+ int tfd;
+ // Data to be sent to the Activity Manager.
+ std::string* amfd_data;
+ // The tid of the thread that crashed.
+ pid_t crashed_tid;
+ // The tid of the thread we are currently working with.
+ pid_t current_tid;
+ // logd daemon crash, can block asking for logcat data, allow suppression.
+ bool should_retrieve_logcat;
- log_t()
- : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
- should_retrieve_logcat(true) {}
+ log_t()
+ : tfd(-1),
+ amfd_data(nullptr),
+ crashed_tid(-1),
+ current_tid(-1),
+ should_retrieve_logcat(true) {}
};
// List of types of logs to simplify the logging decision in _LOG
@@ -59,13 +62,20 @@
};
// Log information onto the tombstone.
-void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
- __attribute__ ((format(printf, 3, 4)));
+void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
+namespace unwindstack {
+class Memory;
+}
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
+void dump_memory(log_t* log, unwindstack::Memory* backtrace, uintptr_t addr, const char* fmt, ...);
void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
+void drop_capabilities();
+
+bool signal_has_si_addr(int si_signo, int si_code);
+const char* get_signame(int sig);
+const char* get_sigcode(int signo, int code);
+
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
deleted file mode 100644
index 1fc690b..0000000
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- pt_regs r;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
- for (int reg = 0; reg < 32; reg++) {
- // skip uninteresting registers
- if (reg == 0 // $0
- || reg == 26 // $k0
- || reg == 27 // $k1
- || reg == 31 // $ra (done below)
- )
- continue;
-
- dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]);
- }
-
- uintptr_t pc = R(r.cp0_epc);
- uintptr_t ra = R(r.regs[31]);
- dump_memory(log, backtrace, pc, "code around pc:");
- if (pc != ra) {
- dump_memory(log, backtrace, ra, "code around ra:");
- }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
- pt_regs r;
- if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR " at %08" PRIxPTR
- " v0 %08" PRIxPTR " v1 %08" PRIxPTR "\n",
- R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
- _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR " a1 %08" PRIxPTR
- " a2 %08" PRIxPTR " a3 %08" PRIxPTR "\n",
- R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
- _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR " t1 %08" PRIxPTR
- " t2 %08" PRIxPTR " t3 %08" PRIxPTR "\n",
- R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
- _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR " t5 %08" PRIxPTR
- " t6 %08" PRIxPTR " t7 %08" PRIxPTR "\n",
- R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
- _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR " s1 %08" PRIxPTR
- " s2 %08" PRIxPTR " s3 %08" PRIxPTR "\n",
- R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
- _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR " s5 %08" PRIxPTR
- " s6 %08" PRIxPTR " s7 %08" PRIxPTR "\n",
- R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
- _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR " t9 %08" PRIxPTR
- " k0 %08" PRIxPTR " k1 %08" PRIxPTR "\n",
- R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
- _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR " sp %08" PRIxPTR
- " s8 %08" PRIxPTR " ra %08" PRIxPTR "\n",
- R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
- _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR " lo %08" PRIxPTR
- " bva %08" PRIxPTR " epc %08" PRIxPTR "\n",
- R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
deleted file mode 100644
index 955e507..0000000
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- pt_regs r;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
- for (int reg = 0; reg < 32; reg++) {
- // skip uninteresting registers
- if (reg == 0 // $0
- || reg == 26 // $k0
- || reg == 27 // $k1
- || reg == 31 // $ra (done below)
- )
- continue;
-
- dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]);
- }
-
- uintptr_t pc = R(r.cp0_epc);
- uintptr_t ra = R(r.regs[31]);
- dump_memory(log, backtrace, pc, "code around pc:");
- if (pc != ra) {
- dump_memory(log, backtrace, ra, "code around ra:");
- }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
- pt_regs r;
- if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR " at %016" PRIxPTR
- " v0 %016" PRIxPTR " v1 %016" PRIxPTR "\n",
- R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
- _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR " a1 %016" PRIxPTR
- " a2 %016" PRIxPTR " a3 %016" PRIxPTR "\n",
- R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
- _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR " a5 %016" PRIxPTR
- " a6 %016" PRIxPTR " a7 %016" PRIxPTR "\n",
- R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
- _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR " t1 %016" PRIxPTR
- " t2 %016" PRIxPTR " t3 %016" PRIxPTR "\n",
- R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
- _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR " s1 %016" PRIxPTR
- " s2 %016" PRIxPTR " s3 %016" PRIxPTR "\n",
- R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
- _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR " s5 %016" PRIxPTR
- " s6 %016" PRIxPTR " s7 %016" PRIxPTR "\n",
- R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
- _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR " t9 %016" PRIxPTR
- " k0 %016" PRIxPTR " k1 %016" PRIxPTR "\n",
- R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
- _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR " sp %016" PRIxPTR
- " s8 %016" PRIxPTR " ra %016" PRIxPTR "\n",
- R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
- _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR " lo %016" PRIxPTR
- " bva %016" PRIxPTR " epc %016" PRIxPTR "\n",
- R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index e199db8..b12703e 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -61,7 +61,7 @@
}
}
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) {
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {
for (auto& file : files) {
_LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
}
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
index 6104f7e..e7dbed7 100644
--- a/debuggerd/libdebuggerd/test/BacktraceMock.h
+++ b/debuggerd/libdebuggerd/test/BacktraceMock.h
@@ -17,15 +17,6 @@
#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ucontext.h>
-
-#include <string>
-#include <vector>
-
-#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
class BacktraceMapMock : public BacktraceMap {
@@ -38,69 +29,4 @@
}
};
-
-class BacktraceMock : public Backtrace {
- public:
- explicit BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
- if (map_ == nullptr) {
- abort();
- }
- }
- virtual ~BacktraceMock() {}
-
- virtual bool Unwind(size_t, ucontext_t*) { return false; }
- virtual bool ReadWord(uintptr_t, word_t*) { return false;}
-
- virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
-
- virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
- size_t offset = 0;
- if (last_read_addr_ > 0) {
- offset = addr - last_read_addr_;
- }
- size_t bytes_available = buffer_.size() - offset;
-
- if (do_partial_read_) {
- // Do a partial read.
- if (bytes > bytes_partial_read_) {
- bytes = bytes_partial_read_;
- }
- bytes_partial_read_ -= bytes;
- // Only support a single partial read.
- do_partial_read_ = false;
- } else if (bytes > bytes_available) {
- bytes = bytes_available;
- }
-
- if (bytes > 0) {
- memcpy(buffer, buffer_.data() + offset, bytes);
- }
-
- last_read_addr_ = addr;
- return bytes;
- }
-
- void SetReadData(uint8_t* buffer, size_t bytes) {
- buffer_.resize(bytes);
- memcpy(buffer_.data(), buffer, bytes);
- bytes_partial_read_ = 0;
- do_partial_read_ = false;
- last_read_addr_ = 0;
- }
-
- void SetPartialReadAmount(size_t bytes) {
- if (bytes > buffer_.size()) {
- abort();
- }
- bytes_partial_read_ = bytes;
- do_partial_read_ = true;
- }
-
- private:
- std::vector<uint8_t> buffer_;
- size_t bytes_partial_read_ = 0;
- uintptr_t last_read_addr_ = 0;
- bool do_partial_read_ = false;
-};
-
#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 0fad2cf..7c8a0ea 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -19,12 +19,12 @@
#include <memory>
#include <string>
-#include <gtest/gtest.h>
#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <unwindstack/Memory.h>
#include "libdebuggerd/utility.h"
-#include "BacktraceMock.h"
#include "log_fake.h"
const char g_expected_full_dump[] =
@@ -103,11 +103,59 @@
" 123456d0 -------- -------- -------- -------- ................\n";
#endif
+class MemoryMock : public unwindstack::Memory {
+ public:
+ virtual ~MemoryMock() = default;
+
+ virtual size_t Read(uint64_t addr, void* buffer, size_t bytes) override {
+ size_t offset = 0;
+ if (last_read_addr_ > 0) {
+ offset = addr - last_read_addr_;
+ }
+ size_t bytes_available = buffer_.size() - offset;
+
+ if (partial_read_) {
+ bytes = std::min(bytes, bytes_partial_read_);
+ bytes_partial_read_ -= bytes;
+ partial_read_ = bytes_partial_read_;
+ } else if (bytes > bytes_available) {
+ bytes = bytes_available;
+ }
+
+ if (bytes > 0) {
+ memcpy(buffer, buffer_.data() + offset, bytes);
+ }
+
+ last_read_addr_ = addr;
+ return bytes;
+ }
+
+ void SetReadData(uint8_t* buffer, size_t bytes) {
+ buffer_.resize(bytes);
+ memcpy(buffer_.data(), buffer, bytes);
+ bytes_partial_read_ = 0;
+ last_read_addr_ = 0;
+ }
+
+ void SetPartialReadAmount(size_t bytes) {
+ if (bytes > buffer_.size()) {
+ abort();
+ }
+ partial_read_ = true;
+ bytes_partial_read_ = bytes;
+ }
+
+ private:
+ std::vector<uint8_t> buffer_;
+ bool partial_read_ = false;
+ size_t bytes_partial_read_ = 0;
+ uintptr_t last_read_addr_ = 0;
+};
+
class DumpMemoryTest : public ::testing::Test {
protected:
virtual void SetUp() {
- map_mock_.reset(new BacktraceMapMock());
- backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+ memory_mock_ = std::make_unique<MemoryMock>();
char tmp_file[256];
const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -138,10 +186,10 @@
if (log_.tfd >= 0) {
close(log_.tfd);
}
+ memory_mock_.reset();
}
- std::unique_ptr<BacktraceMapMock> map_mock_;
- std::unique_ptr<BacktraceMock> backtrace_mock_;
+ std::unique_ptr<MemoryMock> memory_mock_;
log_t log_;
};
@@ -151,9 +199,9 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
- dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -170,10 +218,10 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(96);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(96);
- dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -190,9 +238,9 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
- dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -205,7 +253,7 @@
}
TEST_F(DumpMemoryTest, memory_unreadable) {
- dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+ dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc:");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -259,9 +307,9 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+ dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -278,10 +326,10 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(102);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(102);
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+ dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -303,10 +351,10 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(45);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(45);
- dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+ dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -330,9 +378,9 @@
TEST_F(DumpMemoryTest, address_low_fence) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
- dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near %.2s:", "r1");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -384,9 +432,9 @@
TEST_F(DumpMemoryTest, memory_address_too_low) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
- dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0, "memory near %.2s:", "r1");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -401,16 +449,16 @@
TEST_F(DumpMemoryTest, memory_address_too_high) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
#else
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
#endif
std::string tombstone_contents;
@@ -426,12 +474,12 @@
TEST_F(DumpMemoryTest, memory_address_would_overflow) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
#else
- dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+ dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
#endif
std::string tombstone_contents;
@@ -449,12 +497,12 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
#else
- dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
#endif
std::string tombstone_contents;
@@ -509,12 +557,12 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(0);
size_t page_size = sysconf(_SC_PAGE_SIZE);
uintptr_t addr = 0x10000020 + page_size - 120;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -568,12 +616,12 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(0);
size_t page_size = sysconf(_SC_PAGE_SIZE);
uintptr_t addr = 0x10000020 + page_size - 192;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -627,11 +675,11 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(0);
uintptr_t addr = 0x10000020;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -685,13 +733,13 @@
for (size_t i = 0; i < sizeof(buffer); i++) {
buffer[i] = i;
}
- backtrace_mock_->SetReadData(buffer, sizeof(buffer));
- backtrace_mock_->SetPartialReadAmount(0);
+ memory_mock_->SetReadData(buffer, sizeof(buffer));
+ memory_mock_->SetPartialReadAmount(0);
size_t page_size = sysconf(_SC_PAGE_SIZE);
uintptr_t addr = 0x10000020 + page_size - 256;
- dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+ dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index f8cbca7..9b8281a 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -20,7 +20,9 @@
#include <string>
-class Backtrace;
+namespace unwindstack {
+class Memory;
+}
std::string g_build_id;
@@ -28,7 +30,7 @@
g_build_id = build_id;
}
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string* build_id) {
if (g_build_id != "") {
*build_id = g_build_id;
return true;
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
deleted file mode 100644
index 0d4080e..0000000
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * 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 "ptrace_fake.h"
-
-#include <errno.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <sys/ptrace.h>
-
-#include <string>
-
-siginfo_t g_fake_si = {.si_signo = 0};
-
-void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
- g_fake_si = si;
-}
-
-#if !defined(__BIONIC__)
-extern "C" long ptrace_fake(enum __ptrace_request request, ...) {
-#else
-extern "C" long ptrace_fake(int request, ...) {
-#endif
- if (request == PTRACE_GETSIGINFO) {
- if (g_fake_si.si_signo == 0) {
- errno = EFAULT;
- return -1;
- }
-
- va_list ap;
- va_start(ap, request);
- va_arg(ap, int);
- va_arg(ap, int);
- siginfo_t* si = va_arg(ap, siginfo*);
- va_end(ap);
- *si = g_fake_si;
- return 0;
- }
- return -1;
-}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 59a43b7..1e3a10f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -29,11 +29,6 @@
#include "elf_fake.h"
#include "host_signal_fixup.h"
#include "log_fake.h"
-#include "ptrace_fake.h"
-
-// In order to test this code, we need to include the tombstone.cpp code.
-// Including it, also allows us to override the ptrace function.
-#define ptrace ptrace_fake
#include "tombstone.cpp"
@@ -50,7 +45,6 @@
protected:
virtual void SetUp() {
map_mock_.reset(new BacktraceMapMock());
- backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
char tmp_file[256];
const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -77,11 +71,6 @@
resetLogs();
elf_set_fake_build_id("");
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = SIGABRT;
- si.si_code = SI_KERNEL;
- ptrace_set_fake_getsiginfo(si);
}
virtual void TearDown() {
@@ -91,7 +80,6 @@
}
std::unique_ptr<BacktraceMapMock> map_mock_;
- std::unique_ptr<BacktraceMock> backtrace_mock_;
log_t log_;
std::string amfd_data_;
@@ -108,7 +96,7 @@
#endif
map_mock_->AddMap(map);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -143,7 +131,7 @@
map_mock_->AddMap(map);
elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -182,7 +170,7 @@
map_mock_->AddMap(map);
elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -240,7 +228,7 @@
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -294,13 +282,7 @@
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = SIGBUS;
- si.si_code = SI_KERNEL;
- si.si_addr = reinterpret_cast<void*>(0x1000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -352,13 +334,7 @@
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = SIGBUS;
- si.si_code = SI_KERNEL;
- si.si_addr = reinterpret_cast<void*>(0xa533000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -410,13 +386,7 @@
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = SIGBUS;
- si.si_code = SI_KERNEL;
- si.si_addr = reinterpret_cast<void*>(0xa534040);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -466,17 +436,12 @@
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = SIGBUS;
- si.si_code = SI_KERNEL;
#if defined(__LP64__)
- si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+ uintptr_t addr = 0x12345a534040UL;
#else
- si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+ uintptr_t addr = 0xf534040UL;
#endif
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+ dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -503,124 +468,6 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump =
- "\nmemory map (1 entry):\n"
-#if defined(__LP64__)
- " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n";
-#else
- " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- ASSERT_STREQ("", amfd_data_.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- for (int i = 1; i < 255; i++) {
- ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- si.si_signo = i;
- si.si_code = SI_KERNEL;
- si.si_addr = reinterpret_cast<void*>(0x1000);
- ptrace_set_fake_getsiginfo(si);
- dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- bool has_addr = false;
- switch (si.si_signo) {
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- case SIGTRAP:
- has_addr = true;
- break;
- }
-
- const char* expected_addr_dump = \
-"\nmemory map (1 entry):\n"
-#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
-#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-" 0a434000-0a434fff -w- 0 1000\n";
-#endif
- const char* expected_dump = \
-"\nmemory map (1 entry):\n"
-#if defined(__LP64__)
-" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
-#else
-" 0a434000-0a434fff -w- 0 1000\n";
-#endif
- if (has_addr) {
- ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
- << "Signal " << si.si_signo << " expected to include an address.";
- } else {
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
- << "Signal " << si.si_signo << " is not expected to include an address.";
- }
-
- ASSERT_STREQ("", amfd_data_.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
- }
-}
-
-TEST_F(TombstoneTest, dump_signal_info_error) {
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- ptrace_set_fake_getsiginfo(si);
-
- dump_signal_info(&log_, 123);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
-
- ASSERT_STREQ("", amfd_data_.c_str());
-}
-
TEST_F(TombstoneTest, dump_log_file_error) {
log_.should_retrieve_logcat = true;
dump_log_file(&log_, 123, "/fake/filename", 10);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index a0ba81b..99da801 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -35,8 +35,10 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <backtrace/Backtrace.h>
@@ -44,169 +46,27 @@
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
// Needed to get DEBUGGER_SIGNAL.
#include "debuggerd/handler.h"
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/elf_utils.h"
-#include "libdebuggerd/machine.h"
#include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/utility.h"
using android::base::GetBoolProperty;
using android::base::GetProperty;
using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using unwindstack::Memory;
+using unwindstack::Regs;
#define STACK_WORDS 16
-static bool signal_has_si_addr(int si_signo, int si_code) {
- // Manually sent signals won't have si_addr.
- if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
- return false;
- }
-
- switch (si_signo) {
- case SIGBUS:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- case SIGTRAP:
- return true;
- default:
- return false;
- }
-}
-
-static const char* get_signame(int sig) {
- switch (sig) {
- case SIGABRT: return "SIGABRT";
- case SIGBUS: return "SIGBUS";
- case SIGFPE: return "SIGFPE";
- case SIGILL: return "SIGILL";
- case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
- case SIGSTKFLT: return "SIGSTKFLT";
-#endif
- case SIGSTOP: return "SIGSTOP";
- case SIGSYS: return "SIGSYS";
- case SIGTRAP: return "SIGTRAP";
- case DEBUGGER_SIGNAL: return "<debuggerd signal>";
- default: return "?";
- }
-}
-
-static const char* get_sigcode(int signo, int code) {
- // Try the signal-specific codes...
- switch (signo) {
- case SIGILL:
- switch (code) {
- case ILL_ILLOPC: return "ILL_ILLOPC";
- case ILL_ILLOPN: return "ILL_ILLOPN";
- case ILL_ILLADR: return "ILL_ILLADR";
- case ILL_ILLTRP: return "ILL_ILLTRP";
- case ILL_PRVOPC: return "ILL_PRVOPC";
- case ILL_PRVREG: return "ILL_PRVREG";
- case ILL_COPROC: return "ILL_COPROC";
- case ILL_BADSTK: return "ILL_BADSTK";
- }
- static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
- break;
- case SIGBUS:
- switch (code) {
- case BUS_ADRALN: return "BUS_ADRALN";
- case BUS_ADRERR: return "BUS_ADRERR";
- case BUS_OBJERR: return "BUS_OBJERR";
- case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
- case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
- }
- static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
- break;
- case SIGFPE:
- switch (code) {
- case FPE_INTDIV: return "FPE_INTDIV";
- case FPE_INTOVF: return "FPE_INTOVF";
- case FPE_FLTDIV: return "FPE_FLTDIV";
- case FPE_FLTOVF: return "FPE_FLTOVF";
- case FPE_FLTUND: return "FPE_FLTUND";
- case FPE_FLTRES: return "FPE_FLTRES";
- case FPE_FLTINV: return "FPE_FLTINV";
- case FPE_FLTSUB: return "FPE_FLTSUB";
- }
- static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
- break;
- case SIGSEGV:
- switch (code) {
- case SEGV_MAPERR: return "SEGV_MAPERR";
- case SEGV_ACCERR: return "SEGV_ACCERR";
-#if defined(SEGV_BNDERR)
- case SEGV_BNDERR: return "SEGV_BNDERR";
-#endif
-#if defined(SEGV_PKUERR)
- case SEGV_PKUERR: return "SEGV_PKUERR";
-#endif
- }
-#if defined(SEGV_PKUERR)
- static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
-#elif defined(SEGV_BNDERR)
- static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
-#else
- static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
-#endif
- break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
- case SIGSYS:
- switch (code) {
- case SYS_SECCOMP: return "SYS_SECCOMP";
- }
- static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
- break;
-#endif
- case SIGTRAP:
- switch (code) {
- case TRAP_BRKPT: return "TRAP_BRKPT";
- case TRAP_TRACE: return "TRAP_TRACE";
- case TRAP_BRANCH: return "TRAP_BRANCH";
- case TRAP_HWBKPT: return "TRAP_HWBKPT";
- }
- if ((code & 0xff) == SIGTRAP) {
- switch ((code >> 8) & 0xff) {
- case PTRACE_EVENT_FORK:
- return "PTRACE_EVENT_FORK";
- case PTRACE_EVENT_VFORK:
- return "PTRACE_EVENT_VFORK";
- case PTRACE_EVENT_CLONE:
- return "PTRACE_EVENT_CLONE";
- case PTRACE_EVENT_EXEC:
- return "PTRACE_EVENT_EXEC";
- case PTRACE_EVENT_VFORK_DONE:
- return "PTRACE_EVENT_VFORK_DONE";
- case PTRACE_EVENT_EXIT:
- return "PTRACE_EVENT_EXIT";
- case PTRACE_EVENT_SECCOMP:
- return "PTRACE_EVENT_SECCOMP";
- case PTRACE_EVENT_STOP:
- return "PTRACE_EVENT_STOP";
- }
- }
- static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
- break;
- }
- // Then the other codes...
- switch (code) {
- case SI_USER: return "SI_USER";
- case SI_KERNEL: return "SI_KERNEL";
- case SI_QUEUE: return "SI_QUEUE";
- case SI_TIMER: return "SI_TIMER";
- case SI_MESGQ: return "SI_MESGQ";
- case SI_ASYNCIO: return "SI_ASYNCIO";
- case SI_SIGIO: return "SI_SIGIO";
- case SI_TKILL: return "SI_TKILL";
- case SI_DETHREAD: return "SI_DETHREAD";
- }
- // Then give up...
- return "?";
-}
-
static void dump_header_info(log_t* log) {
auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
auto revision = GetProperty("ro.revision", "unknown");
@@ -216,73 +76,64 @@
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
-static void dump_probable_cause(log_t* log, const siginfo_t& si) {
+static void dump_probable_cause(log_t* log, const siginfo_t* si) {
std::string cause;
- if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
- if (si.si_addr < reinterpret_cast<void*>(4096)) {
+ if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
+ if (si->si_addr < reinterpret_cast<void*>(4096)) {
cause = StringPrintf("null pointer dereference");
- } else if (si.si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+ } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
cause = "call to kuser_helper_version";
- } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+ } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
cause = "call to kuser_get_tls";
- } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+ } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
cause = "call to kuser_cmpxchg";
- } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+ } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
cause = "call to kuser_memory_barrier";
- } else if (si.si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+ } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
cause = "call to kuser_cmpxchg64";
}
- } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
- cause = StringPrintf("seccomp prevented call to disallowed %s system call %d",
- ABI_STRING, si.si_syscall);
+ } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+ cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
+ si->si_syscall);
}
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
}
-static void dump_signal_info(log_t* log, const siginfo_t* siginfo) {
- const siginfo_t& si = *siginfo;
+static void dump_signal_info(log_t* log, const siginfo_t* si) {
char addr_desc[32]; // ", fault addr 0x1234"
- if (signal_has_si_addr(si.si_signo, si.si_code)) {
- snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+ if (signal_has_si_addr(si->si_signo, si->si_code)) {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", si->si_addr);
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
}
- _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo,
- get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc);
+ _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si->si_signo,
+ get_signame(si->si_signo), si->si_code, get_sigcode(si->si_signo, si->si_code), addr_desc);
dump_probable_cause(log, si);
}
-static void dump_signal_info(log_t* log, pid_t tid) {
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
- ALOGE("cannot get siginfo: %s\n", strerror(errno));
- return;
- }
-
- dump_signal_info(log, &si);
-}
-
-static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, const char* process_name,
- const char* thread_name) {
+static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
// Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
// TODO: Why is this controlled by thread name?
- if (strcmp(thread_name, "logd") == 0 || strncmp(thread_name, "logd.", 4) == 0) {
+ if (thread_info.thread_name == "logd" ||
+ android::base::StartsWith(thread_info.thread_name, "logd.")) {
log->should_retrieve_logcat = false;
}
- _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, thread_name,
- process_name);
+ _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
+ thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
}
-static void dump_stack_segment(
- Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+ uintptr_t* sp, size_t words, int label) {
// Read the data all at once.
word_t stack_data[words];
- size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+
+ // TODO: Do we need to word align this for crashes caused by a misaligned sp?
+ // The process_vm_readv implementation of Memory should handle this appropriately?
+ size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words);
words = bytes_read / sizeof(word_t);
std::string line;
for (size_t i = 0; i < words; i++) {
@@ -296,11 +147,11 @@
line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]);
backtrace_map_t map;
- backtrace->FillInMap(stack_data[i], &map);
+ backtrace_map->FillIn(stack_data[i], &map);
if (BacktraceMap::IsValid(map) && !map.name.empty()) {
line += " " + map.name;
uintptr_t offset = 0;
- std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map));
+ std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
if (!func_name.empty()) {
line += " (" + func_name;
if (offset) {
@@ -315,36 +166,38 @@
}
}
-static void dump_stack(Backtrace* backtrace, log_t* log) {
+static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+ std::vector<backtrace_frame_data_t>& frames) {
size_t first = 0, last;
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
- if (frame->sp) {
+ for (size_t i = 0; i < frames.size(); i++) {
+ const backtrace_frame_data_t& frame = frames[i];
+ if (frame.sp) {
if (!first) {
first = i+1;
}
last = i;
}
}
+
if (!first) {
return;
}
first--;
// Dump a few words before the first frame.
- word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
- dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
+ word_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
+ dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
// Dump a few words from all successive frames.
// Only log the first 3 frames, put the rest in the tombstone.
for (size_t i = first; i <= last; i++) {
- const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+ const backtrace_frame_data_t* frame = &frames[i];
if (sp != frame->sp) {
_LOG(log, logtype::STACK, " ........ ........\n");
sp = frame->sp;
}
if (i == last) {
- dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
+ dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
if (sp < frame->sp + frame->stack_size) {
_LOG(log, logtype::STACK, " ........ ........\n");
}
@@ -355,7 +208,7 @@
} else if (words > STACK_WORDS) {
words = STACK_WORDS;
}
- dump_stack_segment(backtrace, log, &sp, words, i);
+ dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
}
}
}
@@ -372,44 +225,34 @@
return addr_str;
}
-static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+static void dump_abort_message(log_t* log, Memory* process_memory, uintptr_t address) {
if (address == 0) {
return;
}
- address += sizeof(size_t); // Skip the buffer length.
+ size_t length;
+ if (!process_memory->ReadFully(address, &length, sizeof(length))) {
+ _LOG(log, logtype::HEADER, "Failed to read abort message header: %s\n", strerror(errno));
+ return;
+ }
char msg[512];
- memset(msg, 0, sizeof(msg));
- char* p = &msg[0];
- while (p < &msg[sizeof(msg)]) {
- word_t data;
- size_t len = sizeof(word_t);
- if (!backtrace->ReadWord(address, &data)) {
- break;
- }
- address += sizeof(word_t);
-
- while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
- len--;
- }
+ if (length >= sizeof(msg)) {
+ _LOG(log, logtype::HEADER, "Abort message too long: claimed length = %zd\n", length);
+ return;
}
- msg[sizeof(msg) - 1] = '\0';
+ if (!process_memory->ReadFully(address + sizeof(length), msg, length)) {
+ _LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno));
+ return;
+ }
+
+ msg[length] = '\0';
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
}
-static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
- bool print_fault_address_marker = false;
- uintptr_t addr = 0;
- siginfo_t si;
- memset(&si, 0, sizeof(si));
- if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
- print_fault_address_marker = signal_has_si_addr(si.si_signo, si.si_code);
- addr = reinterpret_cast<uintptr_t>(si.si_addr);
- } else {
- ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
- }
+static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uintptr_t addr) {
+ bool print_fault_address_marker = addr;
ScopedBacktraceMapIteratorLock lock(map);
_LOG(log, logtype::MAPS,
@@ -464,7 +307,7 @@
space_needed = false;
line += " " + entry->name;
std::string build_id;
- if ((entry->flags & PROT_READ) && elf_get_build_id(backtrace, entry->start, &build_id)) {
+ if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
line += " (BuildId: " + build_id + ")";
}
}
@@ -482,50 +325,117 @@
}
}
-static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
- if (backtrace->NumFrames()) {
- _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
- dump_backtrace_to_log(backtrace, log, " ");
-
- _LOG(log, logtype::STACK, "\nstack:\n");
- dump_stack(backtrace, log);
+void dump_backtrace(log_t* log, std::vector<backtrace_frame_data_t>& frames, const char* prefix) {
+ for (auto& frame : frames) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str());
}
}
-// Weak noop implementation, real implementations are in <arch>/machine.cpp.
-__attribute__((weak)) void dump_registers(log_t* log, const ucontext_t*) {
- _LOG(log, logtype::REGISTERS, " register dumping unimplemented on this architecture");
+static void print_register_row(log_t* log,
+ const std::vector<std::pair<std::string, uint64_t>>& registers) {
+ std::string output;
+ for (auto& [name, value] : registers) {
+ output += android::base::StringPrintf(" %-3s %0*" PRIxPTR, name.c_str(),
+ static_cast<int>(2 * sizeof(void*)),
+ static_cast<uintptr_t>(value));
+ }
+
+ _LOG(log, logtype::REGISTERS, " %s\n", output.c_str());
}
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
- const std::string& thread_name, BacktraceMap* map,
- uintptr_t abort_msg_address, bool primary_thread) {
- log->current_tid = tid;
+void dump_registers(log_t* log, Regs* regs) {
+ // Split lr/sp/pc into their own special row.
+ static constexpr size_t column_count = 4;
+ std::vector<std::pair<std::string, uint64_t>> current_row;
+ std::vector<std::pair<std::string, uint64_t>> special_row;
+
+#if defined(__arm__) || defined(__aarch64__)
+ static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+#elif defined(__i386__)
+ static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
+#elif defined(__x86_64__)
+ static constexpr const char* special_registers[] = {"rbp", "rsp", "rip"};
+#else
+ static constexpr const char* special_registers[] = {};
+#endif
+
+ regs->IterateRegisters([log, ¤t_row, &special_row](const char* name, uint64_t value) {
+ auto row = ¤t_row;
+ for (const char* special_name : special_registers) {
+ if (strcmp(special_name, name) == 0) {
+ row = &special_row;
+ break;
+ }
+ }
+
+ row->emplace_back(name, value);
+ if (current_row.size() == column_count) {
+ print_register_row(log, current_row);
+ current_row.clear();
+ }
+ });
+
+ if (!current_row.empty()) {
+ print_register_row(log, current_row);
+ }
+
+ print_register_row(log, special_row);
+}
+
+void dump_memory_and_code(log_t* log, Memory* memory, Regs* regs) {
+ regs->IterateRegisters([log, memory](const char* name, uint64_t value) {
+ dump_memory(log, memory, value, "memory near %s:", name);
+ });
+}
+
+static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
+ const ThreadInfo& thread_info, uintptr_t abort_msg_address,
+ bool primary_thread) {
+ UNUSED(process_memory);
+ log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
}
- dump_thread_info(log, pid, tid, process_name.c_str(), thread_name.c_str());
- dump_signal_info(log, tid);
+ dump_thread_info(log, thread_info);
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
- if (primary_thread) {
- dump_abort_message(backtrace.get(), log, abort_msg_address);
+ if (thread_info.siginfo) {
+ dump_signal_info(log, thread_info.siginfo);
}
- dump_registers(log, tid);
- if (backtrace->Unwind(0)) {
- dump_backtrace_and_stack(backtrace.get(), log);
- } else {
- ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+
+ dump_registers(log, thread_info.registers.get());
+
+ std::vector<backtrace_frame_data_t> frames;
+ if (!Backtrace::Unwind(thread_info.registers.get(), map, &frames, 0, nullptr)) {
+ _LOG(log, logtype::THREAD, "Failed to unwind");
+ return false;
+ }
+
+ if (!frames.empty()) {
+ _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+ dump_backtrace(log, frames, " ");
+
+ _LOG(log, logtype::STACK, "\nstack:\n");
+ dump_stack(log, map, process_memory, frames);
}
if (primary_thread) {
- dump_memory_and_code(log, backtrace.get());
+ dump_abort_message(log, process_memory, abort_msg_address);
+ }
+
+ if (primary_thread) {
+ dump_memory_and_code(log, process_memory, thread_info.registers.get());
if (map) {
- dump_all_maps(backtrace.get(), map, log, tid);
+ uintptr_t addr = 0;
+ siginfo_t* si = thread_info.siginfo;
+ if (signal_has_si_addr(si->si_signo, si->si_code)) {
+ addr = reinterpret_cast<uintptr_t>(si->si_addr);
+ }
+ dump_all_maps(log, map, process_memory, addr);
}
}
log->current_tid = log->crashed_tid;
+ return true;
}
// Reads the contents of the specified log device, filters out the entries
@@ -534,8 +444,7 @@
// If "tail" is non-zero, log the last "tail" number of lines.
static EventTagMap* g_eventTagMap = NULL;
-static void dump_log_file(
- log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
bool first = true;
struct logger_list* logger_list;
@@ -654,56 +563,15 @@
// Dumps the logs generated by the specified pid to the tombstone, from both
// "system" and "main" log devices. Ideally we'd interleave the output.
static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+ if (pid == getpid()) {
+ // Cowardly refuse to dump logs while we're running in-process.
+ return;
+ }
+
dump_log_file(log, pid, "system", tail);
dump_log_file(log, pid, "main", tail);
}
-// Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
- pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
- // don't copy log messages to tombstone unless this is a dev device
- bool want_logs = GetBoolProperty("ro.debuggable", false);
-
- _LOG(log, logtype::HEADER,
- "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
- dump_header_info(log);
- dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
- if (want_logs) {
- dump_logs(log, pid, 5);
- }
-
- for (const auto& it : threads) {
- pid_t thread_tid = it.first;
- const std::string& thread_name = it.second;
-
- if (thread_tid != tid) {
- dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
- }
- }
-
- if (open_files) {
- _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
- dump_open_files_list_to_log(*open_files, log, " ");
- }
-
- if (want_logs) {
- dump_logs(log, pid, 0);
- }
-}
-
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
- std::string* amfd_data) {
- log_t log;
- log.current_tid = tid;
- log.crashed_tid = tid;
- log.tfd = tombstone_fd;
- log.amfd_data = amfd_data;
- dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
-}
-
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext) {
pid_t pid = getpid();
@@ -721,31 +589,69 @@
read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
- _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
- dump_header_info(&log);
- dump_thread_info(&log, pid, tid, thread_name, process_name);
- dump_signal_info(&log, siginfo);
+ std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
- dump_abort_message(backtrace.get(), &log, abort_msg_address);
- dump_registers(&log, ucontext);
+ std::map<pid_t, ThreadInfo> threads;
+ threads[gettid()] = ThreadInfo{
+ .registers = std::move(regs),
+ .tid = tid,
+ .thread_name = thread_name,
+ .pid = pid,
+ .process_name = process_name,
+ .siginfo = siginfo,
+ };
- if (backtrace->Unwind(0, ucontext)) {
- dump_backtrace_and_stack(backtrace.get(), &log);
- } else {
- ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
+ if (!backtrace_map) {
+ ALOGE("failed to create backtrace map");
+ _exit(1);
}
- // TODO: Make this match the format of dump_all_maps above.
- _LOG(&log, logtype::MAPS, "memory map:\n");
- android::base::unique_fd maps_fd(open("/proc/self/maps", O_RDONLY | O_CLOEXEC));
- if (maps_fd == -1) {
- _LOG(&log, logtype::MAPS, " failed to open /proc/self/maps: %s", strerror(errno));
- } else {
- char buf[256];
- ssize_t rc;
- while ((rc = TEMP_FAILURE_RETRY(read(maps_fd.get(), buf, sizeof(buf)))) > 0) {
- android::base::WriteFully(tombstone_fd, buf, rc);
+ std::shared_ptr<Memory> process_memory = backtrace_map->GetProcessMemory();
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(),
+ threads, tid, abort_msg_address, nullptr, nullptr);
+}
+
+void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
+ const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
+ uintptr_t abort_msg_address, OpenFilesList* open_files,
+ std::string* amfd_data) {
+ // don't copy log messages to tombstone unless this is a dev device
+ bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
+
+ log_t log;
+ log.current_tid = target_thread;
+ log.crashed_tid = target_thread;
+ log.tfd = output_fd.get();
+ log.amfd_data = amfd_data;
+
+ _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ dump_header_info(&log);
+
+ auto it = threads.find(target_thread);
+ if (it == threads.end()) {
+ LOG(FATAL) << "failed to find target thread";
+ }
+ dump_thread(&log, map, process_memory, it->second, abort_msg_address, true);
+
+ if (want_logs) {
+ dump_logs(&log, it->second.pid, 5);
+ }
+
+ for (auto& [tid, thread_info] : threads) {
+ if (tid == target_thread) {
+ continue;
}
+
+ dump_thread(&log, map, process_memory, thread_info, 0, false);
+ }
+
+ if (open_files) {
+ _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
+ dump_open_files_list(&log, *open_files, " ");
+ }
+
+ if (want_logs) {
+ dump_logs(&log, it->second.pid, 0);
}
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 1b74652..247d806 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -21,6 +21,8 @@
#include <errno.h>
#include <signal.h>
#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
@@ -34,7 +36,9 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <backtrace/Backtrace.h>
+#include <debuggerd/handler.h>
#include <log/log.h>
+#include <unwindstack/Memory.h>
using android::base::unique_fd;
@@ -117,34 +121,10 @@
}
}
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
- while (true) {
- int status;
- pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
- if (n == -1) {
- ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
- return false;
- } else if (n == tid) {
- if (WIFSTOPPED(status)) {
- if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
- ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
- return false;
- }
- return true;
- } else {
- ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
- // This is the only circumstance under which we can allow a detach
- // to fail with ESRCH, which indicates the tid has exited.
- return false;
- }
- }
- }
-}
-
#define MEMORY_BYTES_TO_DUMP 256
#define MEMORY_BYTES_PER_LINE 16
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
+void dump_memory(log_t* log, unwindstack::Memory* memory, uintptr_t addr, const char* fmt, ...) {
std::string log_msg;
va_list ap;
va_start(ap, fmt);
@@ -172,7 +152,7 @@
// Dump 256 bytes
uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
memset(data, 0, MEMORY_BYTES_TO_DUMP);
- size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+ size_t bytes = memory->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
if (bytes % sizeof(uintptr_t) != 0) {
// This should never happen, but just in case.
ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
@@ -199,8 +179,8 @@
// into a readable map. Only requires one extra read because a map has
// to contain at least one page, and the total number of bytes to dump
// is smaller than a page.
- size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
- sizeof(data) - bytes - start);
+ size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+ sizeof(data) - bytes - start);
bytes += bytes2;
if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
// This should never happen, but we'll try and continue any way.
@@ -264,3 +244,169 @@
}
strcpy(buf, default_value);
}
+
+void drop_capabilities() {
+ __user_cap_header_struct capheader;
+ memset(&capheader, 0, sizeof(capheader));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ __user_cap_data_struct capdata[2];
+ memset(&capdata, 0, sizeof(capdata));
+
+ if (capset(&capheader, &capdata[0]) == -1) {
+ PLOG(FATAL) << "failed to drop capabilities";
+ }
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
+ }
+}
+
+bool signal_has_si_addr(int si_signo, int si_code) {
+ // Manually sent signals won't have si_addr.
+ if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
+ return false;
+ }
+
+ switch (si_signo) {
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGTRAP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const char* get_signame(int sig) {
+ switch (sig) {
+ case SIGABRT: return "SIGABRT";
+ case SIGBUS: return "SIGBUS";
+ case SIGFPE: return "SIGFPE";
+ case SIGILL: return "SIGILL";
+ case SIGSEGV: return "SIGSEGV";
+#if defined(SIGSTKFLT)
+ case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+ case SIGSTOP: return "SIGSTOP";
+ case SIGSYS: return "SIGSYS";
+ case SIGTRAP: return "SIGTRAP";
+ case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+ default: return "?";
+ }
+}
+
+const char* get_sigcode(int signo, int code) {
+ // Try the signal-specific codes...
+ switch (signo) {
+ case SIGILL:
+ switch (code) {
+ case ILL_ILLOPC: return "ILL_ILLOPC";
+ case ILL_ILLOPN: return "ILL_ILLOPN";
+ case ILL_ILLADR: return "ILL_ILLADR";
+ case ILL_ILLTRP: return "ILL_ILLTRP";
+ case ILL_PRVOPC: return "ILL_PRVOPC";
+ case ILL_PRVREG: return "ILL_PRVREG";
+ case ILL_COPROC: return "ILL_COPROC";
+ case ILL_BADSTK: return "ILL_BADSTK";
+ }
+ static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+ break;
+ case SIGBUS:
+ switch (code) {
+ case BUS_ADRALN: return "BUS_ADRALN";
+ case BUS_ADRERR: return "BUS_ADRERR";
+ case BUS_OBJERR: return "BUS_OBJERR";
+ case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+ case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
+ }
+ static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
+ break;
+ case SIGFPE:
+ switch (code) {
+ case FPE_INTDIV: return "FPE_INTDIV";
+ case FPE_INTOVF: return "FPE_INTOVF";
+ case FPE_FLTDIV: return "FPE_FLTDIV";
+ case FPE_FLTOVF: return "FPE_FLTOVF";
+ case FPE_FLTUND: return "FPE_FLTUND";
+ case FPE_FLTRES: return "FPE_FLTRES";
+ case FPE_FLTINV: return "FPE_FLTINV";
+ case FPE_FLTSUB: return "FPE_FLTSUB";
+ }
+ static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+ break;
+ case SIGSEGV:
+ switch (code) {
+ case SEGV_MAPERR: return "SEGV_MAPERR";
+ case SEGV_ACCERR: return "SEGV_ACCERR";
+#if defined(SEGV_BNDERR)
+ case SEGV_BNDERR: return "SEGV_BNDERR";
+#endif
+#if defined(SEGV_PKUERR)
+ case SEGV_PKUERR: return "SEGV_PKUERR";
+#endif
+ }
+#if defined(SEGV_PKUERR)
+ static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
+#elif defined(SEGV_BNDERR)
+ static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
+#else
+ static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
+#endif
+ break;
+#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
+ case SIGSYS:
+ switch (code) {
+ case SYS_SECCOMP: return "SYS_SECCOMP";
+ }
+ static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
+ break;
+#endif
+ case SIGTRAP:
+ switch (code) {
+ case TRAP_BRKPT: return "TRAP_BRKPT";
+ case TRAP_TRACE: return "TRAP_TRACE";
+ case TRAP_BRANCH: return "TRAP_BRANCH";
+ case TRAP_HWBKPT: return "TRAP_HWBKPT";
+ }
+ if ((code & 0xff) == SIGTRAP) {
+ switch ((code >> 8) & 0xff) {
+ case PTRACE_EVENT_FORK:
+ return "PTRACE_EVENT_FORK";
+ case PTRACE_EVENT_VFORK:
+ return "PTRACE_EVENT_VFORK";
+ case PTRACE_EVENT_CLONE:
+ return "PTRACE_EVENT_CLONE";
+ case PTRACE_EVENT_EXEC:
+ return "PTRACE_EVENT_EXEC";
+ case PTRACE_EVENT_VFORK_DONE:
+ return "PTRACE_EVENT_VFORK_DONE";
+ case PTRACE_EVENT_EXIT:
+ return "PTRACE_EVENT_EXIT";
+ case PTRACE_EVENT_SECCOMP:
+ return "PTRACE_EVENT_SECCOMP";
+ case PTRACE_EVENT_STOP:
+ return "PTRACE_EVENT_STOP";
+ }
+ }
+ static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+ break;
+ }
+ // Then the other codes...
+ switch (code) {
+ case SI_USER: return "SI_USER";
+ case SI_KERNEL: return "SI_KERNEL";
+ case SI_QUEUE: return "SI_QUEUE";
+ case SI_TIMER: return "SI_TIMER";
+ case SI_MESGQ: return "SI_MESGQ";
+ case SI_ASYNCIO: return "SI_ASYNCIO";
+ case SI_SIGIO: return "SI_SIGIO";
+ case SI_TKILL: return "SI_TKILL";
+ case SI_DETHREAD: return "SI_DETHREAD";
+ }
+ // Then give up...
+ return "?";
+}
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
deleted file mode 100644
index 09a64cd..0000000
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2006, The Android Open Source Project
- *
- * 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- struct pt_regs r;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
- struct pt_regs r;
- if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n",
- r.eax, r.ebx, r.ecx, r.edx);
- _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n",
- r.esi, r.edi);
- _LOG(log, logtype::REGISTERS, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n",
- r.xcs, r.xds, r.xes, r.xfs, r.xss);
- _LOG(log, logtype::REGISTERS, " eip %08lx ebp %08lx esp %08lx flags %08lx\n",
- r.eip, r.ebp, r.esp, r.eflags);
-}
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
deleted file mode 100644
index de1c268..0000000
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** 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.
-*/
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
- struct user_regs_struct r;
- if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
-
- dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
- struct user_regs_struct r;
- if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
- ALOGE("cannot get registers: %s\n", strerror(errno));
- return;
- }
-
- _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n",
- r.rax, r.rbx, r.rcx, r.rdx);
- _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n",
- r.rsi, r.rdi);
- _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n",
- r.r8, r.r9, r.r10, r.r11);
- _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n",
- r.r12, r.r13, r.r14, r.r15);
- _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n",
- r.cs, r.ss);
- _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n",
- r.rip, r.rbp, r.rsp, r.eflags);
-}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 7e1961e..6903b0e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -16,7 +16,10 @@
#pragma once
+#include <signal.h>
#include <stdint.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
#include "dump_type.h"
@@ -76,3 +79,11 @@
InterceptStatus status;
char error_message[127]; // always null-terminated
};
+
+// Sent from handler to crash_dump via pipe.
+struct __attribute__((__packed__)) CrashInfo {
+ uint32_t version; // must be 1.
+ siginfo_t siginfo;
+ ucontext_t ucontext;
+ uintptr_t abort_msg_address;
+};
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 0bb07ac..50c5efc 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,8 +18,12 @@
#include <sys/socket.h>
+#include <string>
#include <utility>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "protocol.h"
@@ -86,3 +90,15 @@
return result;
}
+
+std::string get_process_name(pid_t pid) {
+ std::string result = "<unknown>";
+ android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
+ return result;
+}
+
+std::string get_thread_name(pid_t tid) {
+ std::string result = "<unknown>";
+ android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
+ return android::base::Trim(result);
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 171e07a..8260b44 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,8 @@
#pragma once
+#include <string>
+
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -42,3 +44,6 @@
// plus any errors returned by the underlying recvmsg.
ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
android::base::unique_fd* _Nullable out_fd);
+
+std::string get_process_name(pid_t pid);
+std::string get_thread_name(pid_t tid);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ed165ed..f23150d 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -25,6 +25,9 @@
"-Werror",
"-Wno-unused-variable",
],
+ cppflags: [
+ "-std=gnu++1z",
+ ],
}
cc_library_static {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2c18a6d..34afed1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -23,6 +23,10 @@
#include <sys/mount.h>
#include <unistd.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -397,16 +401,17 @@
}
static std::string read_fstab_from_dt() {
- std::string fstab;
if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
- return fstab;
+ return {};
}
std::string fstabdir_name = get_android_dt_dir() + "/fstab";
std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
- if (!fstabdir) return fstab;
+ if (!fstabdir) return {};
dirent* dp;
+ // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
+ std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
while ((dp = readdir(fstabdir.get())) != NULL) {
// skip over name, compatible and .
if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
@@ -427,41 +432,54 @@
file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
- fstab.clear();
- break;
+ return {};
}
fstab_entry.push_back(value);
- fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+ std::string mount_point;
+ file_name =
+ android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+ if (read_dt_file(file_name, &value)) {
+ LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
+ mount_point = value;
+ } else {
+ mount_point = android::base::StringPrintf("/%s", dp->d_name);
+ }
+ fstab_entry.push_back(mount_point);
file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
- fstab.clear();
- break;
+ return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
- fstab.clear();
- break;
+ return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
if (!read_dt_file(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
- fstab.clear();
- break;
+ return {};
}
fstab_entry.push_back(value);
-
- fstab += android::base::Join(fstab_entry, " ");
- fstab += '\n';
+ // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
+ fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
}
- return fstab;
+ // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
+ std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
+ [](const auto& a, const auto& b) { return a.first < b.first; });
+
+ std::string fstab_result;
+ for (const auto& [_, dt_entry] : fstab_dt_entries) {
+ fstab_result += dt_entry + "\n";
+ }
+ return fstab_result;
}
bool is_dt_compatible() {
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5d160ee..f3c70de 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -35,6 +35,10 @@
#include <android-base/macros.h>
#include <android-base/strings.h>
+#ifdef __BIONIC__
+#include <android-base/properties.h>
+#endif
+
#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
"%s:%d: %s CHECK '" #predicate "' failed.",\
__FILE__, __LINE__, __FUNCTION__)
@@ -110,6 +114,25 @@
return std::string(debuggable) == "1";
}
+static std::string vndk_version_str() {
+#ifdef __BIONIC__
+ std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+#endif
+ return "";
+}
+
+static void insert_vndk_version_str(std::string* file_name) {
+ CHECK(file_name != nullptr);
+ size_t insert_pos = file_name->find_last_of(".");
+ if (insert_pos == std::string::npos) {
+ insert_pos = file_name->length();
+ }
+ file_name->insert(insert_pos, vndk_version_str());
+}
+
class LibraryNamespaces {
public:
LibraryNamespaces() : initialized_(false) { }
@@ -318,6 +341,10 @@
"Error reading public native library list from \"%s\": %s",
public_native_libraries_system_config.c_str(), error_msg.c_str());
+ // Insert VNDK version to llndk and vndksp config file names.
+ insert_vndk_version_str(&llndk_native_libraries_system_config);
+ insert_vndk_version_str(&vndksp_native_libraries_system_config);
+
// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
// variable to add libraries to the list. This is intended for platform tests only.
if (is_debuggable()) {
@@ -347,11 +374,11 @@
system_public_libraries_ = base::Join(sonames, ':');
sonames.clear();
- ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames);
+ ReadConfig(llndk_native_libraries_system_config, &sonames);
system_llndk_libraries_ = base::Join(sonames, ':');
sonames.clear();
- ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames);
+ ReadConfig(vndksp_native_libraries_system_config, &sonames);
system_vndksp_libraries_ = base::Join(sonames, ':');
sonames.clear();
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 96e1c10..1d6c434 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -40,14 +40,14 @@
}
if (!autosuspend_ops) {
- ALOGE("failed to initialize autosuspend\n");
+ ALOGE("failed to initialize autosuspend");
return -1;
}
out:
autosuspend_inited = true;
- ALOGV("autosuspend initialized\n");
+ ALOGV("autosuspend initialized");
return 0;
}
@@ -60,7 +60,7 @@
return ret;
}
- ALOGV("autosuspend_enable\n");
+ ALOGV("autosuspend_enable");
if (autosuspend_enabled) {
return 0;
@@ -84,7 +84,7 @@
return ret;
}
- ALOGV("autosuspend_disable\n");
+ ALOGV("autosuspend_disable");
if (!autosuspend_enabled) {
return 0;
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 2da204a..0a172be 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -67,36 +67,36 @@
update_sleep_time(success);
usleep(sleep_time);
success = false;
- ALOGV("%s: read wakeup_count\n", __func__);
+ ALOGV("%s: read wakeup_count", __func__);
lseek(wakeup_count_fd, 0, SEEK_SET);
wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
sizeof(wakeup_count)));
if (wakeup_count_len < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ ALOGE("Error reading from %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
wakeup_count_len = 0;
continue;
}
if (!wakeup_count_len) {
- ALOGE("Empty wakeup count\n");
+ ALOGE("Empty wakeup count");
continue;
}
- ALOGV("%s: wait\n", __func__);
+ ALOGV("%s: wait", __func__);
ret = sem_wait(&suspend_lockout);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error waiting on semaphore: %s\n", buf);
+ ALOGE("Error waiting on semaphore: %s", buf);
continue;
}
- ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
+ ALOGV("%s: write %*s to wakeup_count", __func__, wakeup_count_len, wakeup_count);
ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ ALOGE("Error writing to %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
} else {
- ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
+ ALOGV("%s: write %s to %s", __func__, sleep_state, SYS_POWER_STATE);
ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
if (ret >= 0) {
success = true;
@@ -107,11 +107,11 @@
}
}
- ALOGV("%s: release sem\n", __func__);
+ ALOGV("%s: release sem", __func__);
ret = sem_post(&suspend_lockout);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error releasing semaphore: %s\n", buf);
+ ALOGE("Error releasing semaphore: %s", buf);
}
}
return NULL;
@@ -122,16 +122,16 @@
char buf[80];
int ret;
- ALOGV("autosuspend_wakeup_count_enable\n");
+ ALOGV("autosuspend_wakeup_count_enable");
ret = sem_post(&suspend_lockout);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error changing semaphore: %s\n", buf);
+ ALOGE("Error changing semaphore: %s", buf);
}
- ALOGV("autosuspend_wakeup_count_enable done\n");
+ ALOGV("autosuspend_wakeup_count_enable done");
return ret;
}
@@ -141,16 +141,16 @@
char buf[80];
int ret;
- ALOGV("autosuspend_wakeup_count_disable\n");
+ ALOGV("autosuspend_wakeup_count_disable");
ret = sem_wait(&suspend_lockout);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error changing semaphore: %s\n", buf);
+ ALOGE("Error changing semaphore: %s", buf);
}
- ALOGV("autosuspend_wakeup_count_disable done\n");
+ ALOGV("autosuspend_wakeup_count_disable done");
return ret;
}
@@ -177,31 +177,31 @@
state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR));
if (state_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
+ ALOGE("Error opening %s: %s", SYS_POWER_STATE, buf);
goto err_open_state;
}
wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR));
if (wakeup_count_fd < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+ ALOGE("Error opening %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
goto err_open_wakeup_count;
}
ret = sem_init(&suspend_lockout, 0, 0);
if (ret < 0) {
strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error creating semaphore: %s\n", buf);
+ ALOGE("Error creating semaphore: %s", buf);
goto err_sem_init;
}
ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
if (ret) {
strerror_r(ret, buf, sizeof(buf));
- ALOGE("Error creating thread: %s\n", buf);
+ ALOGE("Error creating thread: %s", buf);
goto err_pthread_create;
}
- ALOGI("Selected wakeup count\n");
+ ALOGI("Selected wakeup count");
return &autosuspend_wakeup_count_ops;
err_pthread_create:
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 4125b12..5e94388 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -64,6 +64,8 @@
"RegsArm64.cpp",
"RegsX86.cpp",
"RegsX86_64.cpp",
+ "RegsMips.cpp",
+ "RegsMips64.cpp",
"Unwinder.cpp",
"Symbols.cpp",
],
@@ -86,6 +88,12 @@
x86_64: {
srcs: ["AsmGetRegsX86_64.S"],
},
+ mips: {
+ srcs: ["AsmGetRegsMips.S"],
+ },
+ mips64: {
+ srcs: ["AsmGetRegsMips64.S"],
+ },
},
shared_libs: [
@@ -159,6 +167,7 @@
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
+ "tests/files/offline/gnu_debugdata_arm32/*",
"tests/files/offline/straddle_arm32/*",
"tests/files/offline/straddle_arm64/*",
],
diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S
new file mode 100644
index 0000000..183d0a9
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .type AsmGetRegs, %function
+ .globl AsmGetRegs
+ .ent AsmGetRegs
+ .balign 16
+AsmGetRegs:
+ .cfi_startproc
+ .cfi_def_cfa $sp, 0
+ .set push
+ .set noreorder
+ .cpload $t9
+ sw $zero, 0($a0)
+ .set noat
+ sw $at, 4($a0)
+ .set at
+ sw $v0, 8($a0)
+ sw $v1, 12($a0)
+ sw $a0, 16($a0)
+ sw $a1, 20($a0)
+ sw $a2, 24($a0)
+ sw $a3, 28($a0)
+ sw $t0, 32($a0)
+ sw $t1, 36($a0)
+ sw $t2, 40($a0)
+ sw $t3, 44($a0)
+ sw $t4, 48($a0)
+ sw $t5, 52($a0)
+ sw $t6, 56($a0)
+ sw $t7, 60($a0)
+ sw $s0, 64($a0)
+ sw $s1, 68($a0)
+ sw $s2, 72($a0)
+ sw $s3, 76($a0)
+ sw $s4, 80($a0)
+ sw $s5, 84($a0)
+ sw $s6, 88($a0)
+ sw $s7, 92($a0)
+ sw $t8, 96($a0)
+ sw $t9, 100($a0)
+ sw $k0, 104($a0)
+ sw $k1, 108($a0)
+ sw $gp, 112($a0)
+ sw $sp, 116($a0)
+ sw $s8, 120($a0)
+ sw $ra, 124($a0)
+ jalr $zero, $ra
+ sw $ra, 128($a0) // set PC to the calling function
+
+ .set pop
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
+ .end AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S
new file mode 100644
index 0000000..7a244f6
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips64.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ .text
+ .type AsmGetRegs, %function
+ .globl AsmGetRegs
+ .ent AsmGetRegs
+ .balign 16
+AsmGetRegs:
+ .cfi_startproc
+ .cfi_def_cfa $sp, 0
+ .set push
+ .set noreorder
+ .cpload $t9
+ sd $zero, 0($a0)
+ .set noat
+ sd $at, 8($a0)
+ .set at
+ sd $v0, 16($a0)
+ sd $v1, 24($a0)
+ sd $a0, 32($a0)
+ sd $a1, 40($a0)
+ sd $a2, 48($a0)
+ sd $a3, 56($a0)
+ sd $a4, 64($a0)
+ sd $a5, 72($a0)
+ sd $a6, 80($a0)
+ sd $a7, 88($a0)
+ sd $t0, 96($a0)
+ sd $t1, 104($a0)
+ sd $t2, 112($a0)
+ sd $t3, 120($a0)
+ sd $s0, 128($a0)
+ sd $s1, 136($a0)
+ sd $s2, 144($a0)
+ sd $s3, 152($a0)
+ sd $s4, 160($a0)
+ sd $s5, 168($a0)
+ sd $s6, 176($a0)
+ sd $s7, 184($a0)
+ sd $t8, 192($a0)
+ sd $t9, 200($a0)
+ sd $k0, 208($a0)
+ sd $k1, 216($a0)
+ sd $gp, 224($a0)
+ sd $sp, 232($a0)
+ sd $s8, 240($a0)
+ sd $ra, 248($a0)
+ jalr $zero, $ra
+ sd $ra, 256($a0) // set PC to the calling function
+
+ .set pop
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
+ .end AsmGetRegs
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 025429f..5ec4a3d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -79,6 +79,7 @@
uint64_t load_bias;
if (gnu->Init(&load_bias)) {
gnu->InitHeaders();
+ interface_->SetGnuDebugdataInterface(gnu);
} else {
// Free all of the memory associated with the gnu_debugdata section.
gnu_debugdata_memory_.reset(nullptr);
@@ -115,17 +116,9 @@
return true;
}
- // Adjust the load bias to get the real relative pc.
- if (adjusted_rel_pc < load_bias_) {
- return false;
- }
- adjusted_rel_pc -= load_bias_;
-
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
- (gnu_debugdata_interface_ &&
- gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
+ return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
}
bool Elf::IsValidElf(Memory* memory) {
@@ -189,9 +182,12 @@
} else if (e_machine == EM_386) {
arch_ = ARCH_X86;
interface.reset(new ElfInterface32(memory));
+ } else if (e_machine == EM_MIPS) {
+ arch_ = ARCH_MIPS;
+ interface.reset(new ElfInterface32(memory));
} else {
// Unsupported.
- ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+ ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
return nullptr;
}
} else if (class_type_ == ELFCLASS64) {
@@ -205,9 +201,12 @@
arch_ = ARCH_ARM64;
} else if (e_machine == EM_X86_64) {
arch_ = ARCH_X86_64;
+ } else if (e_machine == EM_MIPS) {
+ arch_ = ARCH_MIPS64;
} else {
// Unsupported.
- ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
+ ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
+ e_machine);
return nullptr;
}
interface.reset(new ElfInterface64(memory));
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 334cf76..df1642e 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -386,16 +386,29 @@
return false;
}
-bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished) {
+ // Adjust the load bias to get the real relative pc.
+ if (pc < load_bias) {
+ return false;
+ }
+ uint64_t adjusted_pc = pc - load_bias;
+
// Try the eh_frame first.
DwarfSection* eh_frame = eh_frame_.get();
- if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
+ if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
return true;
}
// Try the debug_frame next.
DwarfSection* debug_frame = debug_frame_.get();
- if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
+ if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+ return true;
+ }
+
+ // Finally try the gnu_debugdata interface, but always use a zero load bias.
+ if (gnu_debugdata_interface_ != nullptr &&
+ gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
return true;
}
return false;
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 9841e24..5d99bd7 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -92,16 +92,24 @@
return true;
}
-bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished) {
// Dwarf unwind information is precise about whether a pc is covered or not,
// but arm unwind information only has ranges of pc. In order to avoid
// incorrectly doing a bad unwind using arm unwind information for a
// different function, always try and unwind with the dwarf information first.
- return ElfInterface32::Step(pc, regs, process_memory, finished) ||
- StepExidx(pc, regs, process_memory, finished);
+ return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) ||
+ StepExidx(pc, load_bias, regs, process_memory, finished);
}
-bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished) {
+ // Adjust the load bias to get the real relative pc.
+ if (pc < load_bias) {
+ return false;
+ }
+ pc -= load_bias;
+
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index eeb2e17..9c067ba 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,9 +70,11 @@
bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
- bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
+ bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished) override;
- bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+ bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished);
uint64_t start_offset() { return start_offset_; }
diff --git a/libunwindstack/MachineMips.h b/libunwindstack/MachineMips.h
new file mode 100644
index 0000000..2dfb1e9
--- /dev/null
+++ b/libunwindstack/MachineMips.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 _LIBUNWINDSTACK_MACHINE_MIPS_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum MipsReg : uint16_t {
+ MIPS_REG_R0 = 0,
+ MIPS_REG_R1,
+ MIPS_REG_R2,
+ MIPS_REG_R3,
+ MIPS_REG_R4,
+ MIPS_REG_R5,
+ MIPS_REG_R6,
+ MIPS_REG_R7,
+ MIPS_REG_R8,
+ MIPS_REG_R9,
+ MIPS_REG_R10,
+ MIPS_REG_R11,
+ MIPS_REG_R12,
+ MIPS_REG_R13,
+ MIPS_REG_R14,
+ MIPS_REG_R15,
+ MIPS_REG_R16,
+ MIPS_REG_R17,
+ MIPS_REG_R18,
+ MIPS_REG_R19,
+ MIPS_REG_R20,
+ MIPS_REG_R21,
+ MIPS_REG_R22,
+ MIPS_REG_R23,
+ MIPS_REG_R24,
+ MIPS_REG_R25,
+ MIPS_REG_R26,
+ MIPS_REG_R27,
+ MIPS_REG_R28,
+ MIPS_REG_R29,
+ MIPS_REG_R30,
+ MIPS_REG_R31,
+ MIPS_REG_PC,
+ MIPS_REG_LAST,
+
+ MIPS_REG_SP = MIPS_REG_R29,
+ MIPS_REG_RA = MIPS_REG_R31,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_MIPS_H
\ No newline at end of file
diff --git a/libunwindstack/MachineMips64.h b/libunwindstack/MachineMips64.h
new file mode 100644
index 0000000..34addf2
--- /dev/null
+++ b/libunwindstack/MachineMips64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Mips64Reg : uint16_t {
+ MIPS64_REG_R0 = 0,
+ MIPS64_REG_R1,
+ MIPS64_REG_R2,
+ MIPS64_REG_R3,
+ MIPS64_REG_R4,
+ MIPS64_REG_R5,
+ MIPS64_REG_R6,
+ MIPS64_REG_R7,
+ MIPS64_REG_R8,
+ MIPS64_REG_R9,
+ MIPS64_REG_R10,
+ MIPS64_REG_R11,
+ MIPS64_REG_R12,
+ MIPS64_REG_R13,
+ MIPS64_REG_R14,
+ MIPS64_REG_R15,
+ MIPS64_REG_R16,
+ MIPS64_REG_R17,
+ MIPS64_REG_R18,
+ MIPS64_REG_R19,
+ MIPS64_REG_R20,
+ MIPS64_REG_R21,
+ MIPS64_REG_R22,
+ MIPS64_REG_R23,
+ MIPS64_REG_R24,
+ MIPS64_REG_R25,
+ MIPS64_REG_R26,
+ MIPS64_REG_R27,
+ MIPS64_REG_R28,
+ MIPS64_REG_R29,
+ MIPS64_REG_R30,
+ MIPS64_REG_R31,
+ MIPS64_REG_PC,
+ MIPS64_REG_LAST,
+
+ MIPS64_REG_SP = MIPS64_REG_R29,
+ MIPS64_REG_RA = MIPS64_REG_R31,
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MACHINE_MIPS64_H
\ No newline at end of file
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 51bce8e..89fe038 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -122,13 +122,21 @@
}
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+ uint64_t cur_load_bias = load_bias.load();
+ if (cur_load_bias != static_cast<uint64_t>(-1)) {
+ return cur_load_bias;
+ }
+
{
// Make sure no other thread is trying to add the elf to this map.
std::lock_guard<std::mutex> guard(mutex_);
if (elf != nullptr) {
if (elf->valid()) {
- return elf->GetLoadBias();
+ cur_load_bias = elf->GetLoadBias();
+ load_bias = cur_load_bias;
+ return cur_load_bias;
} else {
+ load_bias = 0;
return 0;
}
}
@@ -137,7 +145,9 @@
// Call lightweight static function that will only read enough of the
// elf data to get the load bias.
std::unique_ptr<Memory> memory(CreateMemory(process_memory));
- return Elf::GetLoadBias(memory.get());
+ cur_load_bias = Elf::GetLoadBias(memory.get());
+ load_bias = cur_load_bias;
+ return cur_load_bias;
}
} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 56370c1..4c16212 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -202,6 +202,13 @@
return return_value;
}
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name, uint64_t load_bias) {
+ MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+ map_info->load_bias = load_bias;
+ maps_.push_back(map_info);
+}
+
Maps::~Maps() {
for (auto& map : maps_) {
delete map;
@@ -235,61 +242,4 @@
return "/proc/" + std::to_string(pid_) + "/maps";
}
-bool OfflineMaps::Parse() {
- // Format of maps information:
- // <uint64_t> StartOffset
- // <uint64_t> EndOffset
- // <uint64_t> offset
- // <uint16_t> flags
- // <uint16_t> MapNameLength
- // <VariableLengthValue> MapName
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
- if (fd == -1) {
- return false;
- }
-
- std::vector<char> name;
- while (true) {
- uint64_t start;
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
- if (bytes == 0) {
- break;
- }
- if (bytes == -1 || bytes != sizeof(start)) {
- return false;
- }
- uint64_t end;
- bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
- if (bytes == -1 || bytes != sizeof(end)) {
- return false;
- }
- uint64_t offset;
- bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
- if (bytes == -1 || bytes != sizeof(offset)) {
- return false;
- }
- uint16_t flags;
- bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
- if (bytes == -1 || bytes != sizeof(flags)) {
- return false;
- }
- uint16_t len;
- bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
- if (bytes == -1 || bytes != sizeof(len)) {
- return false;
- }
- if (len > 0) {
- name.resize(len);
- bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
- if (bytes == -1 || bytes != len) {
- return false;
- }
- maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
- } else {
- maps_.push_back(new MapInfo(start, end, offset, flags, ""));
- }
- }
- return true;
-}
-
} // namespace unwindstack
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 29dbf9d..7feafad 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -27,16 +27,20 @@
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
#include "UserArm.h"
#include "UserArm64.h"
#include "UserX86.h"
#include "UserX86_64.h"
+#include "UserMips.h"
+#include "UserMips64.h"
namespace unwindstack {
// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
+constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
// This function assumes that reg_data is already aligned to a 64 bit value.
// If not this could crash with an unaligned access.
@@ -60,6 +64,10 @@
return RegsArm::Read(buffer.data());
case sizeof(arm64_user_regs):
return RegsArm64::Read(buffer.data());
+ case sizeof(mips_user_regs):
+ return RegsMips::Read(buffer.data());
+ case sizeof(mips64_user_regs):
+ return RegsMips64::Read(buffer.data());
}
return nullptr;
}
@@ -74,6 +82,10 @@
return RegsArm::CreateFromUcontext(ucontext);
case ARCH_ARM64:
return RegsArm64::CreateFromUcontext(ucontext);
+ case ARCH_MIPS:
+ return RegsMips::CreateFromUcontext(ucontext);
+ case ARCH_MIPS64:
+ return RegsMips64::CreateFromUcontext(ucontext);
case ARCH_UNKNOWN:
default:
return nullptr;
@@ -89,6 +101,10 @@
return ARCH_X86;
#elif defined(__x86_64__)
return ARCH_X86_64;
+#elif defined(__mips__) && !defined(__LP64__)
+ return ARCH_MIPS;
+#elif defined(__mips__) && defined(__LP64__)
+ return ARCH_MIPS64;
#else
abort();
#endif
@@ -104,6 +120,10 @@
regs = new RegsX86();
#elif defined(__x86_64__)
regs = new RegsX86_64();
+#elif defined(__mips__) && !defined(__LP64__)
+ regs = new RegsMips();
+#elif defined(__mips__) && defined(__LP64__)
+ regs = new RegsMips64();
#else
abort();
#endif
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
new file mode 100644
index 0000000..44cde05
--- /dev/null
+++ b/libunwindstack/RegsMips.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips.h>
+
+#include "MachineMips.h"
+#include "UcontextMips.h"
+#include "UserMips.h"
+
+namespace unwindstack {
+
+RegsMips::RegsMips()
+ : RegsImpl<uint32_t>(MIPS_REG_LAST, MIPS_REG_SP, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+
+ArchEnum RegsMips::Arch() {
+ return ARCH_MIPS;
+}
+
+uint64_t RegsMips::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid()) {
+ return rel_pc;
+ }
+
+ // For now, just assuming no compact branches
+ if (rel_pc < 8) {
+ return rel_pc;
+ }
+ return rel_pc - 8;
+}
+
+void RegsMips::SetFromRaw() {
+ set_pc(regs_[MIPS_REG_PC]);
+ set_sp(regs_[MIPS_REG_SP]);
+}
+
+bool RegsMips::SetPcFromReturnAddress(Memory*) {
+ if (pc() == regs_[MIPS_REG_RA]) {
+ return false;
+ }
+
+ set_pc(regs_[MIPS_REG_RA]);
+ return true;
+}
+
+void RegsMips::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("r0", regs_[MIPS_REG_R0]);
+ fn("r1", regs_[MIPS_REG_R1]);
+ fn("r2", regs_[MIPS_REG_R2]);
+ fn("r3", regs_[MIPS_REG_R3]);
+ fn("r4", regs_[MIPS_REG_R4]);
+ fn("r5", regs_[MIPS_REG_R5]);
+ fn("r6", regs_[MIPS_REG_R6]);
+ fn("r7", regs_[MIPS_REG_R7]);
+ fn("r8", regs_[MIPS_REG_R8]);
+ fn("r9", regs_[MIPS_REG_R9]);
+ fn("r10", regs_[MIPS_REG_R10]);
+ fn("r11", regs_[MIPS_REG_R11]);
+ fn("r12", regs_[MIPS_REG_R12]);
+ fn("r13", regs_[MIPS_REG_R13]);
+ fn("r14", regs_[MIPS_REG_R14]);
+ fn("r15", regs_[MIPS_REG_R15]);
+ fn("r16", regs_[MIPS_REG_R16]);
+ fn("r17", regs_[MIPS_REG_R17]);
+ fn("r18", regs_[MIPS_REG_R18]);
+ fn("r19", regs_[MIPS_REG_R19]);
+ fn("r20", regs_[MIPS_REG_R20]);
+ fn("r21", regs_[MIPS_REG_R21]);
+ fn("r22", regs_[MIPS_REG_R22]);
+ fn("r23", regs_[MIPS_REG_R23]);
+ fn("r24", regs_[MIPS_REG_R24]);
+ fn("r25", regs_[MIPS_REG_R25]);
+ fn("r26", regs_[MIPS_REG_R26]);
+ fn("r27", regs_[MIPS_REG_R27]);
+ fn("r28", regs_[MIPS_REG_R28]);
+ fn("sp", regs_[MIPS_REG_SP]);
+ fn("r30", regs_[MIPS_REG_R30]);
+ fn("ra", regs_[MIPS_REG_RA]);
+ fn("pc", regs_[MIPS_REG_PC]);
+}
+
+Regs* RegsMips::Read(void* remote_data) {
+ mips_user_regs* user = reinterpret_cast<mips_user_regs*>(remote_data);
+ RegsMips* regs = new RegsMips();
+ uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+
+ memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t));
+
+ reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC];
+ regs->SetFromRaw();
+ return regs;
+}
+
+Regs* RegsMips::CreateFromUcontext(void* ucontext) {
+ mips_ucontext_t* mips_ucontext = reinterpret_cast<mips_ucontext_t*>(ucontext);
+
+ RegsMips* regs = new RegsMips();
+ // Copy 64 bit sc_regs over to 32 bit regs
+ for (int i = 0; i < 32; i++) {
+ (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
+ }
+ (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
+ regs->SetFromRaw();
+ return regs;
+}
+
+bool RegsMips::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ uint64_t offset = 0;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn functions.
+ // __vdso_rt_sigreturn:
+ // 0x24021061 li v0, 0x1061
+ // 0x0000000c syscall
+ // __vdso_sigreturn:
+ // 0x24021017 li v0, 0x1017
+ // 0x0000000c syscall
+ if (data == 0x0000000c24021061ULL) {
+ // vdso_rt_sigreturn => read rt_sigframe
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+ offset = 24 + 128 + 24 + 8;
+ } else if (data == 0x0000000c24021017LL) {
+ // vdso_sigreturn => read sigframe
+ // offset = sigcontext offset + sc_pc offset
+ offset = 24 + 8;
+ } else {
+ return false;
+ }
+
+ // read sc_pc and sc_regs[32] from stack
+ uint64_t values[MIPS_REG_LAST];
+ if (!process_memory->Read(sp() + offset, values, sizeof(values))) {
+ return false;
+ }
+
+ // Copy 64 bit sc_pc over to 32 bit regs_[MIPS_REG_PC]
+ regs_[MIPS_REG_PC] = values[0];
+
+ // Copy 64 bit sc_regs over to 32 bit regs
+ for (int i = 0; i < 32; i++) {
+ regs_[MIPS_REG_R0 + i] = values[1 + i];
+ }
+
+ SetFromRaw();
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
new file mode 100644
index 0000000..b4e5246
--- /dev/null
+++ b/libunwindstack/RegsMips64.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips64.h>
+
+#include "MachineMips64.h"
+#include "UcontextMips64.h"
+#include "UserMips64.h"
+
+namespace unwindstack {
+
+RegsMips64::RegsMips64()
+ : RegsImpl<uint64_t>(MIPS64_REG_LAST, MIPS64_REG_SP,
+ Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+
+ArchEnum RegsMips64::Arch() {
+ return ARCH_MIPS64;
+}
+
+uint64_t RegsMips64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid()) {
+ return rel_pc;
+ }
+
+ // For now, just assuming no compact branches
+ if (rel_pc < 8) {
+ return rel_pc;
+ }
+ return rel_pc - 8;
+}
+
+void RegsMips64::SetFromRaw() {
+ set_pc(regs_[MIPS64_REG_PC]);
+ set_sp(regs_[MIPS64_REG_SP]);
+}
+
+bool RegsMips64::SetPcFromReturnAddress(Memory*) {
+ if (pc() == regs_[MIPS64_REG_RA]) {
+ return false;
+ }
+
+ set_pc(regs_[MIPS64_REG_RA]);
+ return true;
+}
+
+void RegsMips64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+ fn("r0", regs_[MIPS64_REG_R0]);
+ fn("r1", regs_[MIPS64_REG_R1]);
+ fn("r2", regs_[MIPS64_REG_R2]);
+ fn("r3", regs_[MIPS64_REG_R3]);
+ fn("r4", regs_[MIPS64_REG_R4]);
+ fn("r5", regs_[MIPS64_REG_R5]);
+ fn("r6", regs_[MIPS64_REG_R6]);
+ fn("r7", regs_[MIPS64_REG_R7]);
+ fn("r8", regs_[MIPS64_REG_R8]);
+ fn("r9", regs_[MIPS64_REG_R9]);
+ fn("r10", regs_[MIPS64_REG_R10]);
+ fn("r11", regs_[MIPS64_REG_R11]);
+ fn("r12", regs_[MIPS64_REG_R12]);
+ fn("r13", regs_[MIPS64_REG_R13]);
+ fn("r14", regs_[MIPS64_REG_R14]);
+ fn("r15", regs_[MIPS64_REG_R15]);
+ fn("r16", regs_[MIPS64_REG_R16]);
+ fn("r17", regs_[MIPS64_REG_R17]);
+ fn("r18", regs_[MIPS64_REG_R18]);
+ fn("r19", regs_[MIPS64_REG_R19]);
+ fn("r20", regs_[MIPS64_REG_R20]);
+ fn("r21", regs_[MIPS64_REG_R21]);
+ fn("r22", regs_[MIPS64_REG_R22]);
+ fn("r23", regs_[MIPS64_REG_R23]);
+ fn("r24", regs_[MIPS64_REG_R24]);
+ fn("r25", regs_[MIPS64_REG_R25]);
+ fn("r26", regs_[MIPS64_REG_R26]);
+ fn("r27", regs_[MIPS64_REG_R27]);
+ fn("r28", regs_[MIPS64_REG_R28]);
+ fn("sp", regs_[MIPS64_REG_SP]);
+ fn("r30", regs_[MIPS64_REG_R30]);
+ fn("ra", regs_[MIPS64_REG_RA]);
+ fn("pc", regs_[MIPS64_REG_PC]);
+}
+
+Regs* RegsMips64::Read(void* remote_data) {
+ mips64_user_regs* user = reinterpret_cast<mips64_user_regs*>(remote_data);
+ RegsMips64* regs = new RegsMips64();
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+
+ memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t));
+
+ reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC];
+ regs->SetFromRaw();
+ return regs;
+}
+
+Regs* RegsMips64::CreateFromUcontext(void* ucontext) {
+ mips64_ucontext_t* mips64_ucontext = reinterpret_cast<mips64_ucontext_t*>(ucontext);
+
+ RegsMips64* regs = new RegsMips64();
+ // Copy 64 bit sc_regs over to 64 bit regs
+ memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t));
+ (*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc;
+ regs->SetFromRaw();
+ return regs;
+}
+
+bool RegsMips64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+ uint64_t data;
+ Memory* elf_memory = elf->memory();
+ // Read from elf memory since it is usually more expensive to read from
+ // process memory.
+ if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ return false;
+ }
+
+ // Look for the kernel sigreturn function.
+ // __vdso_rt_sigreturn:
+ // 0x2402145b li v0, 0x145b
+ // 0x0000000c syscall
+ if (data != 0x0000000c2402145bULL) {
+ return false;
+ }
+
+ // vdso_rt_sigreturn => read rt_sigframe
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset
+ // read 64 bit sc_regs[32] from stack into 64 bit regs_
+ if (!process_memory->Read(sp() + 24 + 128 + 40, regs_.data(),
+ sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) {
+ return false;
+ }
+
+ // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+ // read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC]
+ if (!process_memory->Read(sp() + 24 + 128 + 40 + 576, ®s_[MIPS64_REG_PC],
+ sizeof(uint64_t))) {
+ return false;
+ }
+
+ SetFromRaw();
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/UcontextMips.h b/libunwindstack/UcontextMips.h
new file mode 100644
index 0000000..27185e7
--- /dev/null
+++ b/libunwindstack/UcontextMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+
+#include <stdint.h>
+
+#include "MachineMips.h"
+
+namespace unwindstack {
+
+struct mips_stack_t {
+ uint32_t ss_sp; // void __user*
+ uint32_t ss_size; // size_t
+ int32_t ss_flags; // int
+};
+
+struct mips_mcontext_t {
+ uint32_t sc_regmask;
+ uint32_t sc_status;
+ uint64_t sc_pc;
+ uint64_t sc_regs[32];
+ // Nothing else is used, so don't define it.
+};
+
+struct mips_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ mips_stack_t uc_stack;
+ mips_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/UcontextMips64.h b/libunwindstack/UcontextMips64.h
new file mode 100644
index 0000000..623bf3a
--- /dev/null
+++ b/libunwindstack/UcontextMips64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+
+#include <stdint.h>
+
+#include "MachineMips64.h"
+
+namespace unwindstack {
+
+struct mips64_stack_t {
+ uint64_t ss_sp; // void __user*
+ uint64_t ss_size; // size_t
+ int32_t ss_flags; // int
+};
+
+struct mips64_mcontext_t {
+ uint64_t sc_regs[32];
+ uint64_t sc_fpregs[32];
+ uint64_t sc_mdhi;
+ uint64_t sc_hi1;
+ uint64_t sc_hi2;
+ uint64_t sc_hi3;
+ uint64_t sc_mdlo;
+ uint64_t sc_lo1;
+ uint64_t sc_lo2;
+ uint64_t sc_lo3;
+ uint64_t sc_pc;
+ // Nothing else is used, so don't define it.
+};
+
+struct mips64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ mips64_stack_t uc_stack;
+ mips64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS6464_H
diff --git a/libunwindstack/UcontextX86_64.h b/libunwindstack/UcontextX86_64.h
index d689796..2b8bdc4 100644
--- a/libunwindstack/UcontextX86_64.h
+++ b/libunwindstack/UcontextX86_64.h
@@ -38,6 +38,7 @@
struct x86_64_stack_t {
uint64_t ss_sp; // void __user*
int32_t ss_flags; // int
+ int32_t pad;
uint64_t ss_size; // size_t
};
diff --git a/libunwindstack/UserMips.h b/libunwindstack/UserMips.h
new file mode 100644
index 0000000..184be4f
--- /dev/null
+++ b/libunwindstack/UserMips.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS_H
+#define _LIBUNWINDSTACK_USER_MIPS_H
+
+namespace unwindstack {
+
+enum Mips32UserReg : uint16_t {
+ MIPS32_EF_R0 = 6,
+ MIPS32_EF_CP0_EPC = 40,
+};
+
+struct mips_user_regs {
+ uint32_t regs[45];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/UserMips64.h b/libunwindstack/UserMips64.h
new file mode 100644
index 0000000..c46befd
--- /dev/null
+++ b/libunwindstack/UserMips64.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
+#define _LIBUNWINDSTACK_USER_MIPS64_H
+
+namespace unwindstack {
+
+enum Mips64UserReg : uint16_t {
+ MIPS64_EF_R0 = 0,
+ MIPS64_EF_CP0_EPC = 34,
+};
+
+struct mips64_user_regs {
+ uint64_t regs[45];
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index d27727b..a85e5f4 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -42,6 +42,8 @@
ARCH_ARM64,
ARCH_X86,
ARCH_X86_64,
+ ARCH_MIPS,
+ ARCH_MIPS64,
};
class Elf {
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5cfe74d..5d3cd5e 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -60,7 +60,8 @@
virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
uint64_t* offset) = 0;
- virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+ virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+ bool* finished);
Memory* CreateGnuDebugdataMemory();
@@ -68,6 +69,8 @@
const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
+ void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
+
uint64_t dynamic_offset() { return dynamic_offset_; }
uint64_t dynamic_size() { return dynamic_size_; }
uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
@@ -134,6 +137,8 @@
std::unique_ptr<DwarfSection> eh_frame_;
std::unique_ptr<DwarfSection> debug_frame_;
+ // The Elf object owns the gnu_debugdata interface object.
+ ElfInterface* gnu_debugdata_interface_ = nullptr;
std::vector<Symbols*> symbols_;
};
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 6f8ceca..22e48f7 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <atomic>
#include <mutex>
#include <string>
@@ -33,7 +34,12 @@
MapInfo() = default;
MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
- : start(start), end(end), offset(offset), flags(flags), name(name) {}
+ : start(start),
+ end(end),
+ offset(offset),
+ flags(flags),
+ name(name),
+ load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() { delete elf; }
uint64_t start = 0;
@@ -48,6 +54,8 @@
// instead of a portion of the file.
uint64_t elf_offset = 0;
+ std::atomic_uint64_t load_bias;
+
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 34fef7f..17a2d28 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -42,6 +42,9 @@
virtual const std::string GetMapsFile() const { return ""; }
+ void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
+ uint64_t load_bias);
+
typedef std::vector<MapInfo*>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
@@ -100,14 +103,6 @@
const std::string file_;
};
-class OfflineMaps : public FileMaps {
- public:
- OfflineMaps(const std::string& file) : FileMaps(file) {}
- virtual ~OfflineMaps() = default;
-
- bool Parse() override;
-};
-
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index c59e081..557eace 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -87,7 +87,7 @@
regs->SetFromRaw();
}
-#elif defined(__i386__) || defined(__x86_64__)
+#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
extern "C" void AsmGetRegs(void* regs);
@@ -97,11 +97,6 @@
regs->SetFromRaw();
}
-#elif defined(__mips__)
-
-// Stub to allow mips to build.
-void RegsGetLocal(Regs*) {}
-
#endif
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
new file mode 100644
index 0000000..3fe6a9f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 _LIBUNWINDSTACK_REGS_MIPS_H
+#define _LIBUNWINDSTACK_REGS_MIPS_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips : public RegsImpl<uint32_t> {
+ public:
+ RegsMips();
+ virtual ~RegsMips() = default;
+
+ virtual ArchEnum Arch() override final;
+
+ uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
new file mode 100644
index 0000000..6b4bcdf
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 _LIBUNWINDSTACK_REGS_MIPS64_H
+#define _LIBUNWINDSTACK_REGS_MIPS64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips64 : public RegsImpl<uint64_t> {
+ public:
+ RegsMips64();
+ virtual ~RegsMips64() = default;
+
+ virtual ArchEnum Arch() override final;
+
+ uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
+
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+ bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+ virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ static Regs* Read(void* data);
+
+ static Regs* CreateFromUcontext(void* ucontext);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index b94a8a4..68de797 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,7 +43,7 @@
return true;
}
-bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
if (steps_.empty()) {
return false;
}
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 565b13f..abf9927 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,8 +68,7 @@
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
- bool Step(uint64_t, Regs*, Memory*, bool*) override;
-
+ bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 5f7cf60..e6763ab 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -302,7 +302,7 @@
// FindEntry fails.
bool finished;
- ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
// ExtractEntry should fail.
interface.FakeSetStartOffset(0x1000);
@@ -315,20 +315,26 @@
regs[ARM_REG_LR] = 0x20000;
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
// Eval should fail.
memory_.SetData32(0x1004, 0x81000000);
- ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
// Everything should pass.
memory_.SetData32(0x1004, 0x80b0b0b0);
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_FALSE(finished);
ASSERT_EQ(0x1000U, regs.sp());
ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
ASSERT_EQ(0x20000U, regs.pc());
ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
+
+ // Load bias is non-zero.
+ ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, ®s, &process_memory_, &finished));
+
+ // Pc too small.
+ ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, ®s, &process_memory_, &finished));
}
TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
@@ -349,7 +355,7 @@
// Everything should pass.
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_FALSE(finished);
ASSERT_EQ(0x10004U, regs.sp());
ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
@@ -372,7 +378,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -394,7 +400,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_TRUE(finished);
ASSERT_EQ(0x10000U, regs.sp());
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -420,7 +426,7 @@
regs.set_pc(0x1234);
bool finished;
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
@@ -432,7 +438,7 @@
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(interface.StepExidx(0x7000, 0, ®s, &process_memory_, &finished));
ASSERT_TRUE(finished);
ASSERT_EQ(0U, regs.pc());
}
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 00192f1..5e808ef 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -145,7 +145,7 @@
ASSERT_FALSE(elf.Init(false));
ASSERT_EQ("", GetFakeLogBuf());
- ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
+ ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
GetFakeLogPrint());
}
@@ -158,7 +158,7 @@
ASSERT_FALSE(elf.Init(false));
ASSERT_EQ("", GetFakeLogBuf());
- ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
+ ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
GetFakeLogPrint());
}
@@ -174,6 +174,18 @@
ASSERT_TRUE(elf.interface() != nullptr);
}
+TEST_F(ElfTest, elf_mips) {
+ Elf elf(memory_);
+
+ InitElf32(EM_MIPS);
+
+ ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+ ASSERT_EQ(ELFCLASS32, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
TEST_F(ElfTest, elf_x86) {
Elf elf(memory_);
@@ -210,6 +222,18 @@
ASSERT_TRUE(elf.interface() != nullptr);
}
+TEST_F(ElfTest, elf_mips64) {
+ Elf elf(memory_);
+
+ InitElf64(EM_MIPS);
+
+ ASSERT_TRUE(elf.Init(false));
+ ASSERT_TRUE(elf.valid());
+ ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+ ASSERT_EQ(ELFCLASS64, elf.class_type());
+ ASSERT_TRUE(elf.interface() != nullptr);
+}
+
TEST_F(ElfTest, gnu_debugdata_init_fail32) {
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -322,7 +346,7 @@
void InitHeaders() override {}
bool GetSoname(std::string*) override { return false; }
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
- MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
+ MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
};
TEST_F(ElfTest, step_in_interface) {
@@ -337,7 +361,7 @@
MemoryFake process_memory;
bool finished;
- EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
+ EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
@@ -358,7 +382,7 @@
bool finished;
ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
- EXPECT_CALL(*interface, Step(0x3300, ®s, &process_memory, &finished))
+ EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 44a73a8..631036b 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -68,12 +68,23 @@
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
}
+TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
+ map_info_->elf = elf_container_.release();
+
+ elf_->FakeSetLoadBias(0);
+ EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+ elf_->FakeSetLoadBias(0x1000);
+ EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+}
+
TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
map_info_->elf = elf_container_.release();
elf_->FakeSetLoadBias(0);
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+ map_info_->load_bias = static_cast<uint64_t>(-1);
elf_->FakeSetLoadBias(0x1000);
EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
}
@@ -141,6 +152,15 @@
EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
}
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) {
+ InitElfData(memory_, map_info_->start);
+
+ EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+
+ memory_->Clear();
+ EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
InitElfData(memory_, map_info_->start);
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 8dc884f..9622ba5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -44,6 +44,24 @@
}
}
+TEST(MapsTest, map_add) {
+ Maps maps;
+
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+ maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+ ASSERT_EQ(3U, maps.Total());
+ MapInfo* info = maps.Get(0);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("fake_map", info->name);
+ ASSERT_EQ(0U, info->elf_offset);
+ ASSERT_EQ(0U, info->load_bias.load());
+}
+
TEST(MapsTest, verify_parse_line) {
MapInfo info;
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 0cb70ba..8b5b31f 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -29,11 +29,15 @@
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
#include "MachineArm.h"
#include "MachineArm64.h"
#include "MachineX86.h"
#include "MachineX86_64.h"
+#include "MachineMips.h"
+#include "MachineMips64.h"
namespace unwindstack {
@@ -152,7 +156,87 @@
return result;
}
-using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64>;
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips>() {
+ std::vector<Register> result;
+ result.push_back({"r0", MIPS_REG_R0});
+ result.push_back({"r1", MIPS_REG_R1});
+ result.push_back({"r2", MIPS_REG_R2});
+ result.push_back({"r3", MIPS_REG_R3});
+ result.push_back({"r4", MIPS_REG_R4});
+ result.push_back({"r5", MIPS_REG_R5});
+ result.push_back({"r6", MIPS_REG_R6});
+ result.push_back({"r7", MIPS_REG_R7});
+ result.push_back({"r8", MIPS_REG_R8});
+ result.push_back({"r9", MIPS_REG_R9});
+ result.push_back({"r10", MIPS_REG_R10});
+ result.push_back({"r11", MIPS_REG_R11});
+ result.push_back({"r12", MIPS_REG_R12});
+ result.push_back({"r13", MIPS_REG_R13});
+ result.push_back({"r14", MIPS_REG_R14});
+ result.push_back({"r15", MIPS_REG_R15});
+ result.push_back({"r16", MIPS_REG_R16});
+ result.push_back({"r17", MIPS_REG_R17});
+ result.push_back({"r18", MIPS_REG_R18});
+ result.push_back({"r19", MIPS_REG_R19});
+ result.push_back({"r20", MIPS_REG_R20});
+ result.push_back({"r21", MIPS_REG_R21});
+ result.push_back({"r22", MIPS_REG_R22});
+ result.push_back({"r23", MIPS_REG_R23});
+ result.push_back({"r24", MIPS_REG_R24});
+ result.push_back({"r25", MIPS_REG_R25});
+ result.push_back({"r26", MIPS_REG_R26});
+ result.push_back({"r27", MIPS_REG_R27});
+ result.push_back({"r28", MIPS_REG_R28});
+ result.push_back({"sp", MIPS_REG_SP});
+ result.push_back({"r30", MIPS_REG_R30});
+ result.push_back({"ra", MIPS_REG_RA});
+ result.push_back({"pc", MIPS_REG_PC});
+
+ return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips64>() {
+ std::vector<Register> result;
+ result.push_back({"r0", MIPS64_REG_R0});
+ result.push_back({"r1", MIPS64_REG_R1});
+ result.push_back({"r2", MIPS64_REG_R2});
+ result.push_back({"r3", MIPS64_REG_R3});
+ result.push_back({"r4", MIPS64_REG_R4});
+ result.push_back({"r5", MIPS64_REG_R5});
+ result.push_back({"r6", MIPS64_REG_R6});
+ result.push_back({"r7", MIPS64_REG_R7});
+ result.push_back({"r8", MIPS64_REG_R8});
+ result.push_back({"r9", MIPS64_REG_R9});
+ result.push_back({"r10", MIPS64_REG_R10});
+ result.push_back({"r11", MIPS64_REG_R11});
+ result.push_back({"r12", MIPS64_REG_R12});
+ result.push_back({"r13", MIPS64_REG_R13});
+ result.push_back({"r14", MIPS64_REG_R14});
+ result.push_back({"r15", MIPS64_REG_R15});
+ result.push_back({"r16", MIPS64_REG_R16});
+ result.push_back({"r17", MIPS64_REG_R17});
+ result.push_back({"r18", MIPS64_REG_R18});
+ result.push_back({"r19", MIPS64_REG_R19});
+ result.push_back({"r20", MIPS64_REG_R20});
+ result.push_back({"r21", MIPS64_REG_R21});
+ result.push_back({"r22", MIPS64_REG_R22});
+ result.push_back({"r23", MIPS64_REG_R23});
+ result.push_back({"r24", MIPS64_REG_R24});
+ result.push_back({"r25", MIPS64_REG_R25});
+ result.push_back({"r26", MIPS64_REG_R26});
+ result.push_back({"r27", MIPS64_REG_R27});
+ result.push_back({"r28", MIPS64_REG_R28});
+ result.push_back({"sp", MIPS64_REG_SP});
+ result.push_back({"r30", MIPS64_REG_R30});
+ result.push_back({"ra", MIPS64_REG_RA});
+ result.push_back({"pc", MIPS64_REG_PC});
+
+ return result;
+}
+
+using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
TYPED_TEST_CASE(RegsIterateTest, RegTypes);
TYPED_TEST(RegsIterateTest, iterate) {
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index ae57caf..ef9e61c 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -23,11 +23,15 @@
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
#include "MachineArm.h"
#include "MachineArm64.h"
#include "MachineX86.h"
#include "MachineX86_64.h"
+#include "MachineMips.h"
+#include "MachineMips64.h"
#include "MemoryFake.h"
@@ -204,4 +208,64 @@
EXPECT_EQ(0x150U, regs.pc());
}
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_non_rt) {
+ uint64_t addr = 0x1000;
+ RegsMips regs;
+ regs[MIPS_REG_PC] = 0x8000;
+ regs[MIPS_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
+
+ for (uint64_t index = 0; index <= 50; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x220U, regs[MIPS_REG_SP]);
+ EXPECT_EQ(0x040U, regs[MIPS_REG_PC]);
+ EXPECT_EQ(0x220U, regs.sp());
+ EXPECT_EQ(0x040U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_rt) {
+ uint64_t addr = 0x1000;
+ RegsMips regs;
+ regs[MIPS_REG_PC] = 0x8000;
+ regs[MIPS_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[MIPS_REG_SP]);
+ EXPECT_EQ(0x170U, regs[MIPS_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x170U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips64_step_if_signal_handler) {
+ uint64_t addr = 0x1000;
+ RegsMips64 regs;
+ regs[MIPS64_REG_PC] = 0x8000;
+ regs[MIPS64_REG_SP] = addr;
+ regs.SetFromRaw();
+
+ elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
+
+ for (uint64_t index = 0; index <= 100; index++) {
+ process_memory_.SetData64(addr + index * 8, index * 0x10);
+ }
+
+ ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+ EXPECT_EQ(0x350U, regs[MIPS64_REG_SP]);
+ EXPECT_EQ(0x600U, regs[MIPS64_REG_PC]);
+ EXPECT_EQ(0x350U, regs.sp());
+ EXPECT_EQ(0x600U, regs.pc());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index a932973..3f84890 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -25,6 +25,8 @@
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
#include "ElfFake.h"
#include "MemoryFake.h"
@@ -112,6 +114,30 @@
ASSERT_EQ(0x1U, x86_64.GetAdjustedPc(0x2, elf_.get()));
ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x1, elf_.get()));
ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x0, elf_.get()));
+
+ RegsMips mips;
+ ASSERT_EQ(0x8U, mips.GetAdjustedPc(0x10, elf_.get()));
+ ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x8, elf_.get()));
+ ASSERT_EQ(0x7U, mips.GetAdjustedPc(0x7, elf_.get()));
+ ASSERT_EQ(0x6U, mips.GetAdjustedPc(0x6, elf_.get()));
+ ASSERT_EQ(0x5U, mips.GetAdjustedPc(0x5, elf_.get()));
+ ASSERT_EQ(0x4U, mips.GetAdjustedPc(0x4, elf_.get()));
+ ASSERT_EQ(0x3U, mips.GetAdjustedPc(0x3, elf_.get()));
+ ASSERT_EQ(0x2U, mips.GetAdjustedPc(0x2, elf_.get()));
+ ASSERT_EQ(0x1U, mips.GetAdjustedPc(0x1, elf_.get()));
+ ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x0, elf_.get()));
+
+ RegsMips64 mips64;
+ ASSERT_EQ(0x8U, mips64.GetAdjustedPc(0x10, elf_.get()));
+ ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x8, elf_.get()));
+ ASSERT_EQ(0x7U, mips64.GetAdjustedPc(0x7, elf_.get()));
+ ASSERT_EQ(0x6U, mips64.GetAdjustedPc(0x6, elf_.get()));
+ ASSERT_EQ(0x5U, mips64.GetAdjustedPc(0x5, elf_.get()));
+ ASSERT_EQ(0x4U, mips64.GetAdjustedPc(0x4, elf_.get()));
+ ASSERT_EQ(0x3U, mips64.GetAdjustedPc(0x3, elf_.get()));
+ ASSERT_EQ(0x2U, mips64.GetAdjustedPc(0x2, elf_.get()));
+ ASSERT_EQ(0x1U, mips64.GetAdjustedPc(0x1, elf_.get()));
+ ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x0, elf_.get()));
}
TEST_F(RegsTest, rel_pc_arm) {
@@ -154,6 +180,8 @@
RegsArm64 regs_arm64;
RegsX86 regs_x86;
RegsX86_64 regs_x86_64;
+ RegsMips regs_mips;
+ RegsMips64 regs_mips64;
MapInfo map_info(0x1000, 0x2000);
Elf* invalid_elf = new Elf(new MemoryFake);
map_info.elf = invalid_elf;
@@ -173,6 +201,14 @@
regs_x86_64.set_pc(0x1800);
EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
+
+ regs_mips.set_pc(0x1900);
+ EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
+ EXPECT_EQ(0x900U, regs_mips.GetAdjustedPc(0x900U, invalid_elf));
+
+ regs_mips64.set_pc(0x1a00);
+ EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
+ EXPECT_EQ(0xa00U, regs_mips64.GetAdjustedPc(0xa00U, invalid_elf));
}
TEST_F(RegsTest, arm_set_from_raw) {
@@ -215,6 +251,26 @@
EXPECT_EQ(0x4900000000U, x86_64.pc());
}
+TEST_F(RegsTest, mips_set_from_raw) {
+ RegsMips mips;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
+ regs[29] = 0x100;
+ regs[32] = 0x200;
+ mips.SetFromRaw();
+ EXPECT_EQ(0x100U, mips.sp());
+ EXPECT_EQ(0x200U, mips.pc());
+}
+
+TEST_F(RegsTest, mips64_set_from_raw) {
+ RegsMips64 mips64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
+ regs[29] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ mips64.SetFromRaw();
+ EXPECT_EQ(0xb100000000U, mips64.sp());
+ EXPECT_EQ(0xc200000000U, mips64.pc());
+}
+
TEST_F(RegsTest, machine_type) {
RegsArm arm_regs;
EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
@@ -227,6 +283,12 @@
RegsX86_64 x86_64_regs;
EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch());
+
+ RegsMips mips_regs;
+ EXPECT_EQ(ARCH_MIPS, mips_regs.Arch());
+
+ RegsMips64 mips64_regs;
+ EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch());
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 962f744..8f28036 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -96,6 +96,54 @@
frame_info);
}
+TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm32) {
+ std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm32/");
+
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsArm regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_PC] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_SP] = reg_value;
+ regs.SetFromRaw();
+ fclose(fp);
+
+ fp = fopen((dir + "maps.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ // The file is guaranteed to be less than 4096 bytes.
+ std::vector<char> buffer(4096);
+ ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+ fclose(fp);
+
+ BufferMaps maps(buffer.data());
+ ASSERT_TRUE(maps.Parse());
+
+ ASSERT_EQ(ARCH_ARM, regs.Arch());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0006dc49 libandroid_runtime.so "
+ "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+ " #01 pc 0006dce5 libandroid_runtime.so "
+ "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+ frame_info);
+}
+
TEST(UnwindOfflineTest, pc_straddle_arm64) {
std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2034191..cd46807 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -32,6 +32,8 @@
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
#include <unwindstack/Unwinder.h>
#include "ElfFake.h"
@@ -733,6 +735,16 @@
x86_64->set_sp(0x10000);
reg_list.push_back(x86_64);
+ RegsMips* mips = new RegsMips;
+ mips->set_pc(0x2300);
+ mips->set_sp(0x10000);
+ reg_list.push_back(mips);
+
+ RegsMips64* mips64 = new RegsMips64;
+ mips64->set_pc(0x2300);
+ mips64->set_sp(0x10000);
+ reg_list.push_back(mips64);
+
for (auto regs : reg_list) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
@@ -744,10 +756,12 @@
switch (regs->Arch()) {
case ARCH_ARM:
case ARCH_X86:
+ case ARCH_MIPS:
expected = " #00 pc 00001300 /system/fake/libc.so (Frame0+10)";
break;
case ARCH_ARM64:
case ARCH_X86_64:
+ case ARCH_MIPS64:
expected = " #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)";
break;
default:
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
new file mode 100644
index 0000000..e4283e6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt
new file mode 100644
index 0000000..1bcddb6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt
@@ -0,0 +1 @@
+f1f10000-f2049000 r-xp 00000000 00:00 0 libandroid_runtime.so
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt
new file mode 100644
index 0000000..c6a93dc
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt
@@ -0,0 +1,2 @@
+pc: f1f6dc49
+sp: d8fe6930
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
new file mode 100644
index 0000000..19cdf2d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 7896279..81bedb7 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -76,6 +76,12 @@
case unwindstack::ARCH_X86_64:
printf("x86_64");
break;
+ case unwindstack::ARCH_MIPS:
+ printf("mips");
+ break;
+ case unwindstack::ARCH_MIPS64:
+ printf("mips64");
+ break;
default:
printf("unknown\n");
return;
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a00b2ee..7f2d11d 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -87,7 +87,7 @@
for (const DwarfFde* fde : *section) {
// Sometimes there are entries that have empty length, skip those since
// they don't contain any interesting information.
- if (fde->pc_start == fde->pc_end) {
+ if (fde == nullptr || fde->pc_start == fde->pc_end) {
continue;
}
printf("\n PC 0x%" PRIx64, fde->pc_start + load_bias);
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index cd3e164..46aa5a6 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -84,4 +84,4 @@
}
BENCHMARK(Iterate_all_files);
-BENCHMARK_MAIN()
+BENCHMARK_MAIN();
diff --git a/property_service/.clang-format b/property_service/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/property_service/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/property_service/Android.bp b/property_service/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/property_service/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
new file mode 100644
index 0000000..3e732b5
--- /dev/null
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+ name: "libpropertyinfoparser",
+ srcs: ["property_info_parser.cpp"],
+
+ cpp_std: "experimental",
+ sanitize: {
+ misc_undefined: ["signed-integer-overflow"],
+ },
+ cppflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ stl: "none",
+ export_include_dirs: ["include"],
+}
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
new file mode 100644
index 0000000..8c3507e
--- /dev/null
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -0,0 +1,224 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 PROPERTY_INFO_PARSER_H
+#define PROPERTY_INFO_PARSER_H
+
+#include <stdint.h>
+
+namespace android {
+namespace properties {
+
+// The below structs intentionally do not end with char name[0] or other tricks to allocate
+// with a dynamic size, such that they can be added onto in the future without breaking
+// backwards compatibility.
+struct PropertyEntry {
+ uint32_t name_offset;
+ uint32_t namelen;
+
+ // This is the context match for this node_; ~0u if it doesn't correspond to any.
+ uint32_t context_index;
+ // This is the schema for this node_; ~0u if it doesn't correspond to any.
+ uint32_t schema_index;
+};
+
+struct TrieNodeInternal {
+ // This points to a property entry struct, which includes the name for this node
+ uint32_t property_entry;
+
+ // Children are a sorted list of child nodes_; binary search them.
+ uint32_t num_child_nodes;
+ uint32_t child_nodes;
+
+ // Prefixes are terminating prefix matches at this node, sorted longest to smallest
+ // Take the first match sequentially found with StartsWith().
+ uint32_t num_prefixes;
+ uint32_t prefix_entries;
+
+ // Exact matches are a sorted list of exact matches at this node_; binary search them.
+ uint32_t num_exact_matches;
+ uint32_t exact_match_entries;
+};
+
+struct PropertyInfoAreaHeader {
+ // The current version of this data as created by property service.
+ uint32_t current_version;
+ // The lowest version of libc that can properly parse this data.
+ uint32_t minimum_supported_version;
+ uint32_t size;
+ uint32_t contexts_offset;
+ uint32_t schemas_offset;
+ uint32_t root_offset;
+};
+
+class SerializedData {
+ public:
+ uint32_t size() const {
+ return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;
+ }
+
+ const char* c_string(uint32_t offset) const {
+ if (offset != 0 && offset > size()) return nullptr;
+ return static_cast<const char*>(data_base_ + offset);
+ }
+
+ const uint32_t* uint32_array(uint32_t offset) const {
+ if (offset != 0 && offset > size()) return nullptr;
+ return reinterpret_cast<const uint32_t*>(data_base_ + offset);
+ }
+
+ uint32_t uint32(uint32_t offset) const {
+ if (offset != 0 && offset > size()) return ~0u;
+ return *reinterpret_cast<const uint32_t*>(data_base_ + offset);
+ }
+
+ const char* data_base() const { return data_base_; }
+
+ private:
+ const char data_base_[0];
+};
+
+class TrieNode {
+ public:
+ TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}
+ TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)
+ : serialized_data_(data_base), trie_node_base_(trie_node_base) {}
+
+ const char* name() const {
+ return serialized_data_->c_string(node_property_entry()->name_offset);
+ }
+
+ uint32_t context_index() const { return node_property_entry()->context_index; }
+ uint32_t schema_index() const { return node_property_entry()->schema_index; }
+
+ uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
+ TrieNode child_node(int n) const {
+ uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];
+ const TrieNodeInternal* trie_node_base =
+ reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);
+ return TrieNode(serialized_data_, trie_node_base);
+ }
+
+ bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;
+
+ uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }
+ const PropertyEntry* prefix(int n) const {
+ uint32_t prefix_entry_offset =
+ serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];
+ return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+ prefix_entry_offset);
+ }
+
+ uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }
+ const PropertyEntry* exact_match(int n) const {
+ uint32_t exact_match_entry_offset =
+ serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];
+ return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+ exact_match_entry_offset);
+ }
+
+ private:
+ const PropertyEntry* node_property_entry() const {
+ return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+ trie_node_base_->property_entry);
+ }
+
+ const SerializedData* serialized_data_;
+ const TrieNodeInternal* trie_node_base_;
+};
+
+class PropertyInfoArea : private SerializedData {
+ public:
+ void GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
+ uint32_t* schema_index) const;
+ void GetPropertyInfo(const char* property, const char** context, const char** schema) const;
+
+ int FindContextIndex(const char* context) const;
+ int FindSchemaIndex(const char* schema) const;
+
+ const char* context(uint32_t index) const {
+ uint32_t context_array_size_offset = contexts_offset();
+ const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));
+ return data_base() + context_array[index];
+ }
+
+ const char* schema(uint32_t index) const {
+ uint32_t schema_array_size_offset = schemas_offset();
+ const uint32_t* schema_array = uint32_array(schema_array_size_offset + sizeof(uint32_t));
+ return data_base() + schema_array[index];
+ }
+
+ uint32_t current_version() const { return header()->current_version; }
+ uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }
+
+ uint32_t size() const { return SerializedData::size(); }
+
+ uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
+ uint32_t num_schemas() const { return uint32_array(schemas_offset())[0]; }
+
+ TrieNode root_node() const { return trie(header()->root_offset); }
+
+ private:
+ void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+ uint32_t* context_index, uint32_t* schema_index) const;
+
+ const PropertyInfoAreaHeader* header() const {
+ return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
+ }
+ uint32_t contexts_offset() const { return header()->contexts_offset; }
+ uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
+ uint32_t schemas_offset() const { return header()->schemas_offset; }
+ uint32_t schemas_array_offset() const { return schemas_offset() + sizeof(uint32_t); }
+
+ TrieNode trie(uint32_t offset) const {
+ if (offset != 0 && offset > size()) return TrieNode();
+ const TrieNodeInternal* trie_node_base =
+ reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);
+ return TrieNode(this, trie_node_base);
+ }
+};
+
+// This is essentially a smart pointer for read only mmap region for property contexts.
+class PropertyInfoAreaFile {
+ public:
+ PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}
+ ~PropertyInfoAreaFile() { Reset(); }
+
+ PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;
+ void operator=(const PropertyInfoAreaFile&) = delete;
+ PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;
+ PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;
+
+ bool LoadDefaultPath();
+ bool LoadPath(const char* filename);
+
+ const PropertyInfoArea* operator->() const {
+ return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);
+ }
+
+ explicit operator bool() const { return mmap_base_ != nullptr; }
+
+ void Reset();
+
+ private:
+ void* mmap_base_;
+ size_t mmap_size_;
+};
+
+} // namespace properties
+} // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp
new file mode 100644
index 0000000..e53c625
--- /dev/null
+++ b/property_service/libpropertyinfoparser/property_info_parser.cpp
@@ -0,0 +1,230 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "property_info_parser/property_info_parser.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace properties {
+
+namespace {
+
+// Binary search to find index of element in an array compared via f(search).
+template <typename F>
+int Find(uint32_t array_length, F&& f) {
+ int bottom = 0;
+ int top = array_length - 1;
+ while (top >= bottom) {
+ int search = (top + bottom) / 2;
+
+ auto cmp = f(search);
+
+ if (cmp == 0) return search;
+ if (cmp < 0) bottom = search + 1;
+ if (cmp > 0) top = search - 1;
+ }
+ return -1;
+}
+
+} // namespace
+
+// Binary search the list of contexts to find the index of a given context string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindContextIndex(const char* context) const {
+ return Find(num_contexts(), [this, context](auto array_offset) {
+ auto string_offset = uint32_array(contexts_array_offset())[array_offset];
+ return strcmp(c_string(string_offset), context);
+ });
+}
+
+// Binary search the list of schemas to find the index of a given schema string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindSchemaIndex(const char* schema) const {
+ return Find(num_schemas(), [this, schema](auto array_offset) {
+ auto string_offset = uint32_array(schemas_array_offset())[array_offset];
+ return strcmp(c_string(string_offset), schema);
+ });
+}
+
+// Binary search the list of children nodes to find a TrieNode for a given property piece.
+// Used to traverse the Trie in GetPropertyInfoIndexes().
+bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
+ auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
+ const char* child_name = child_node(array_offset).name();
+ int cmp = strncmp(child_name, name, namelen);
+ if (cmp == 0 && child_name[namelen] != '\0') {
+ // We use strncmp() since name isn't null terminated, but we don't want to match only a
+ // prefix of a child node's name, so we check here if we did only match a prefix and
+ // return 1, to indicate to the binary search to search earlier in the array for the real
+ // match.
+ return 1;
+ }
+ return cmp;
+ });
+
+ if (node_index == -1) {
+ return false;
+ }
+ *child = child_node(node_index);
+ return true;
+}
+
+void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+ uint32_t* context_index, uint32_t* schema_index) const {
+ const uint32_t remaining_name_size = strlen(remaining_name);
+ for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
+ auto prefix_len = trie_node.prefix(i)->namelen;
+ if (prefix_len > remaining_name_size) continue;
+
+ if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
+ *context_index = trie_node.prefix(i)->context_index;
+ *schema_index = trie_node.prefix(i)->schema_index;
+ return;
+ }
+ }
+}
+
+void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
+ uint32_t* schema_index) const {
+ uint32_t return_context_index = ~0u;
+ uint32_t return_schema_index = ~0u;
+ const char* remaining_name = name;
+ auto trie_node = root_node();
+ while (true) {
+ const char* sep = strchr(remaining_name, '.');
+
+ // Apply prefix match for prefix deliminated with '.'
+ if (trie_node.context_index() != ~0u) {
+ return_context_index = trie_node.context_index();
+ }
+ if (trie_node.schema_index() != ~0u) {
+ return_schema_index = trie_node.schema_index();
+ }
+
+ // Check prefixes at this node. This comes after the node check since these prefixes are by
+ // definition longer than the node itself.
+ CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+
+ if (sep == nullptr) {
+ break;
+ }
+
+ const uint32_t substr_size = sep - remaining_name;
+ TrieNode child_node;
+ if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
+ break;
+ }
+
+ trie_node = child_node;
+ remaining_name = sep + 1;
+ }
+
+ // We've made it to a leaf node, so check contents and return appropriately.
+ // Check exact matches
+ for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
+ if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
+ if (context_index != nullptr) *context_index = trie_node.exact_match(i)->context_index;
+ if (schema_index != nullptr) *schema_index = trie_node.exact_match(i)->schema_index;
+ return;
+ }
+ }
+ // Check prefix matches for prefixes not deliminated with '.'
+ CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+ // Return previously found prefix match.
+ if (context_index != nullptr) *context_index = return_context_index;
+ if (schema_index != nullptr) *schema_index = return_schema_index;
+ return;
+}
+
+void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
+ const char** schema) const {
+ uint32_t context_index;
+ uint32_t schema_index;
+ GetPropertyInfoIndexes(property, &context_index, &schema_index);
+ if (context != nullptr) {
+ if (context_index == ~0u) {
+ *context = nullptr;
+ } else {
+ *context = this->context(context_index);
+ }
+ }
+ if (schema != nullptr) {
+ if (schema_index == ~0u) {
+ *schema = nullptr;
+ } else {
+ *schema = this->schema(schema_index);
+ }
+ }
+}
+
+bool PropertyInfoAreaFile::LoadDefaultPath() {
+ return LoadPath("/dev/__properties__/property_info");
+}
+
+bool PropertyInfoAreaFile::LoadPath(const char* filename) {
+ int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+
+ struct stat fd_stat;
+ if (fstat(fd, &fd_stat) < 0) {
+ close(fd);
+ return false;
+ }
+
+ if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
+ ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
+ (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
+ close(fd);
+ return false;
+ }
+
+ auto mmap_size = fd_stat.st_size;
+
+ void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_result == MAP_FAILED) {
+ close(fd);
+ return false;
+ }
+
+ auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
+ if (property_info_area->minimum_supported_version() > 1 ||
+ property_info_area->size() != mmap_size) {
+ munmap(map_result, mmap_size);
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+ mmap_base_ = map_result;
+ mmap_size_ = mmap_size;
+ return true;
+}
+
+void PropertyInfoAreaFile::Reset() {
+ if (mmap_size_ > 0) {
+ munmap(mmap_base_, mmap_size_);
+ }
+ mmap_base_ = nullptr;
+ mmap_size_ = 0;
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
new file mode 100644
index 0000000..20e5e13
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -0,0 +1,38 @@
+cc_defaults {
+ name: "propertyinfoserializer_defaults",
+ cpp_std: "experimental",
+ sanitize: {
+ misc_undefined: ["signed-integer-overflow"],
+ },
+ cppflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpropertyinfoparser",
+ "libbase",
+ ],
+}
+
+cc_library_static {
+ name: "libpropertyinfoserializer",
+ defaults: ["propertyinfoserializer_defaults"],
+ srcs: [
+ "property_info_serializer.cpp",
+ "trie_builder.cpp",
+ "trie_serializer.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "propertyinfoserializer_tests",
+ defaults: ["propertyinfoserializer_defaults"],
+ srcs: [
+ "trie_builder_test.cpp",
+ "property_info_serializer_test.cpp",
+ ],
+ static_libs: ["libpropertyinfoserializer"],
+}
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
new file mode 100644
index 0000000..f7e708e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 PROPERTY_INFO_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyInfoEntry {
+ PropertyInfoEntry() {}
+ template <typename T, typename U, typename V>
+ PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match)
+ : name(std::forward<T>(name)),
+ context(std::forward<U>(context)),
+ schema(std::forward<V>(schema)),
+ exact_match(exact_match) {}
+ std::string name;
+ std::string context;
+ std::string schema;
+ bool exact_match;
+};
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+ const std::string& default_context, const std::string& default_schema,
+ std::string* serialized_trie, std::string* error);
+
+} // namespace properties
+} // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
new file mode 100644
index 0000000..656c96e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <set>
+
+#include "trie_builder.h"
+#include "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+ const std::string& default_context, const std::string& default_schema,
+ std::string* serialized_trie, std::string* error) {
+ // Check that names are legal first
+ auto trie_builder = TrieBuilder(default_context, default_schema);
+
+ for (const auto& [name, context, schema, is_exact] : property_info) {
+ if (!trie_builder.AddToTrie(name, context, schema, is_exact, error)) {
+ return false;
+ }
+ }
+
+ auto trie_serializer = TrieSerializer();
+ *serialized_trie = trie_serializer.SerializeTrie(trie_builder);
+ return true;
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
new file mode 100644
index 0000000..b3fae26
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -0,0 +1,848 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, TrieNodeCheck) {
+ auto property_info = std::vector<PropertyInfoEntry>{
+ {"test.", "1st", "1st", false}, {"test.test", "2nd", "2nd", false},
+
+ {"test.test1", "3rd", "3rd", true}, {"test.test2", "3rd", "3rd", true},
+ {"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
+ };
+
+ auto serialized_trie = std::string();
+ auto build_trie_error = std::string();
+ ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+ << build_trie_error;
+
+ auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+ // Initial checks for property area.
+ EXPECT_EQ(1U, property_info_area->current_version());
+ EXPECT_EQ(1U, property_info_area->minimum_supported_version());
+
+ // Check the root node
+ auto root_node = property_info_area->root_node();
+ EXPECT_STREQ("root", root_node.name());
+ EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+ EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+
+ EXPECT_EQ(0U, root_node.num_prefixes());
+ EXPECT_EQ(0U, root_node.num_exact_matches());
+
+ ASSERT_EQ(2U, root_node.num_child_nodes());
+
+ // Check the 'test'. node
+ TrieNode test_node;
+ ASSERT_TRUE(root_node.FindChildForString("test", 4, &test_node));
+
+ EXPECT_STREQ("test", test_node.name());
+ EXPECT_STREQ("1st", property_info_area->context(test_node.context_index()));
+ EXPECT_STREQ("1st", property_info_area->schema(test_node.schema_index()));
+
+ EXPECT_EQ(0U, test_node.num_child_nodes());
+
+ EXPECT_EQ(1U, test_node.num_prefixes());
+ {
+ auto prefix = test_node.prefix(0);
+ EXPECT_STREQ("test", serialized_trie.data() + prefix->name_offset);
+ EXPECT_EQ(4U, prefix->namelen);
+ EXPECT_STREQ("2nd", property_info_area->context(prefix->context_index));
+ EXPECT_STREQ("2nd", property_info_area->schema(prefix->schema_index));
+ }
+
+ EXPECT_EQ(3U, test_node.num_exact_matches());
+ {
+ auto match1 = test_node.exact_match(0);
+ auto match2 = test_node.exact_match(1);
+ auto match3 = test_node.exact_match(2);
+ EXPECT_STREQ("test1", serialized_trie.data() + match1->name_offset);
+ EXPECT_STREQ("test2", serialized_trie.data() + match2->name_offset);
+ EXPECT_STREQ("test3", serialized_trie.data() + match3->name_offset);
+
+ EXPECT_STREQ("3rd", property_info_area->context(match1->context_index));
+ EXPECT_STREQ("3rd", property_info_area->context(match2->context_index));
+ EXPECT_STREQ("3rd", property_info_area->context(match3->context_index));
+
+ EXPECT_STREQ("3rd", property_info_area->schema(match1->schema_index));
+ EXPECT_STREQ("3rd", property_info_area->schema(match2->schema_index));
+ EXPECT_STREQ("3rd", property_info_area->schema(match3->schema_index));
+ }
+
+ // Check the long string node
+ auto expect_empty_one_child = [](auto& node) {
+ EXPECT_EQ(-1U, node.context_index());
+ EXPECT_EQ(0U, node.num_prefixes());
+ EXPECT_EQ(0U, node.num_exact_matches());
+ EXPECT_EQ(1U, node.num_child_nodes());
+ };
+
+ // Start with 'this'
+ TrieNode long_string_node;
+ ASSERT_TRUE(root_node.FindChildForString("this", 4, &long_string_node));
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'is'
+ ASSERT_TRUE(long_string_node.FindChildForString("is", 2, &long_string_node));
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'a'
+ ASSERT_TRUE(long_string_node.FindChildForString("a", 1, &long_string_node));
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'long'
+ ASSERT_TRUE(long_string_node.FindChildForString("long", 4, &long_string_node));
+ EXPECT_EQ(0U, long_string_node.num_prefixes());
+ EXPECT_EQ(1U, long_string_node.num_exact_matches());
+ EXPECT_EQ(0U, long_string_node.num_child_nodes());
+
+ auto final_match = long_string_node.exact_match(0);
+ EXPECT_STREQ("string", serialized_trie.data() + final_match->name_offset);
+ EXPECT_STREQ("4th", property_info_area->context(final_match->context_index));
+ EXPECT_STREQ("4th", property_info_area->schema(final_match->schema_index));
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo) {
+ auto property_info = std::vector<PropertyInfoEntry>{
+ {"test.", "1st", "1st", false}, {"test.test", "2nd", "2nd", false},
+ {"test.test2.", "6th", "6th", false}, {"test.test", "5th", "5th", true},
+ {"test.test1", "3rd", "3rd", true}, {"test.test2", "7th", "7th", true},
+ {"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
+ {"testoneword", "8th", "8th", true}, {"testwordprefix", "9th", "9th", false},
+ };
+
+ auto serialized_trie = std::string();
+ auto build_trie_error = std::string();
+ ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+ << build_trie_error;
+
+ auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+ // Sanity check
+ auto root_node = property_info_area->root_node();
+ EXPECT_STREQ("root", root_node.name());
+ EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+ EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+
+ const char* context;
+ const char* schema;
+ property_info_area->GetPropertyInfo("abc", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+ property_info_area->GetPropertyInfo("abc.abc", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+ property_info_area->GetPropertyInfo("123.abc", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+
+ property_info_area->GetPropertyInfo("test.a", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("test.b", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("test.c", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+
+ property_info_area->GetPropertyInfo("test.test", &context, &schema);
+ EXPECT_STREQ("5th", context);
+ EXPECT_STREQ("5th", schema);
+ property_info_area->GetPropertyInfo("test.testa", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.testb", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.testc", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+
+ property_info_area->GetPropertyInfo("test.test.a", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.test.b", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.test.c", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+
+ property_info_area->GetPropertyInfo("test.test1", &context, &schema);
+ EXPECT_STREQ("3rd", context);
+ EXPECT_STREQ("3rd", schema);
+ property_info_area->GetPropertyInfo("test.test2", &context, &schema);
+ EXPECT_STREQ("7th", context);
+ EXPECT_STREQ("7th", schema);
+ property_info_area->GetPropertyInfo("test.test3", &context, &schema);
+ EXPECT_STREQ("3rd", context);
+ EXPECT_STREQ("3rd", schema);
+
+ property_info_area->GetPropertyInfo("test.test11", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.test22", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("test.test33", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+
+ property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &schema);
+ EXPECT_STREQ("4th", context);
+ EXPECT_STREQ("4th", schema);
+
+ property_info_area->GetPropertyInfo("this.is.a.long", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+ property_info_area->GetPropertyInfo("this.is.a", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+ property_info_area->GetPropertyInfo("this.is", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+ property_info_area->GetPropertyInfo("this", &context, &schema);
+ EXPECT_STREQ("default", context);
+ EXPECT_STREQ("default", schema);
+
+ property_info_area->GetPropertyInfo("test.test2.a", &context, &schema);
+ EXPECT_STREQ("6th", context);
+ EXPECT_STREQ("6th", schema);
+
+ property_info_area->GetPropertyInfo("testoneword", &context, &schema);
+ EXPECT_STREQ("8th", context);
+ EXPECT_STREQ("8th", schema);
+
+ property_info_area->GetPropertyInfo("testwordprefix", &context, &schema);
+ EXPECT_STREQ("9th", context);
+ EXPECT_STREQ("9th", schema);
+
+ property_info_area->GetPropertyInfo("testwordprefixblah", &context, &schema);
+ EXPECT_STREQ("9th", context);
+ EXPECT_STREQ("9th", schema);
+
+ property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &schema);
+ EXPECT_STREQ("9th", context);
+ EXPECT_STREQ("9th", schema);
+}
+
+TEST(propertyinfoserializer, RealProperties) {
+ auto property_info = std::vector<PropertyInfoEntry>{
+ // Contexts from system/sepolicy/private/property_contexts
+ {"net.rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.gprs", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.ppp", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.qmi", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.lte", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.cdma", "u:object_r:net_radio_prop:s0", "string", false},
+ {"net.dns", "u:object_r:net_dns_prop:s0", "string", false},
+ {"sys.usb.config", "u:object_r:system_radio_prop:s0", "string", false},
+ {"ril.", "u:object_r:radio_prop:s0", "string", false},
+ {"ro.ril.", "u:object_r:radio_prop:s0", "string", false},
+ {"gsm.", "u:object_r:radio_prop:s0", "string", false},
+ {"persist.radio", "u:object_r:radio_prop:s0", "string", false},
+
+ {"net.", "u:object_r:system_prop:s0", "string", false},
+ {"dev.", "u:object_r:system_prop:s0", "string", false},
+ {"ro.runtime.", "u:object_r:system_prop:s0", "string", false},
+ {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0", "string", false},
+ {"hw.", "u:object_r:system_prop:s0", "string", false},
+ {"ro.hw.", "u:object_r:system_prop:s0", "string", false},
+ {"sys.", "u:object_r:system_prop:s0", "string", false},
+ {"sys.cppreopt", "u:object_r:cppreopt_prop:s0", "string", false},
+ {"sys.powerctl", "u:object_r:powerctl_prop:s0", "string", false},
+ {"sys.usb.ffs.", "u:object_r:ffs_prop:s0", "string", false},
+ {"service.", "u:object_r:system_prop:s0", "string", false},
+ {"dhcp.", "u:object_r:dhcp_prop:s0", "string", false},
+ {"dhcp.bt-pan.result", "u:object_r:pan_result_prop:s0", "string", false},
+ {"bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+
+ {"debug.", "u:object_r:debug_prop:s0", "string", false},
+ {"debug.db.", "u:object_r:debuggerd_prop:s0", "string", false},
+ {"dumpstate.", "u:object_r:dumpstate_prop:s0", "string", false},
+ {"dumpstate.options", "u:object_r:dumpstate_options_prop:s0", "string", false},
+ {"log.", "u:object_r:log_prop:s0", "string", false},
+ {"log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+ {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0", "string", false},
+ {"security.perf_harden", "u:object_r:shell_prop:s0", "string", false},
+ {"service.adb.root", "u:object_r:shell_prop:s0", "string", false},
+ {"service.adb.tcp.port", "u:object_r:shell_prop:s0", "string", false},
+
+ {"persist.audio.", "u:object_r:audio_prop:s0", "string", false},
+ {"persist.bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+ {"persist.debug.", "u:object_r:persist_debug_prop:s0", "string", false},
+ {"persist.logd.", "u:object_r:logd_prop:s0", "string", false},
+ {"persist.logd.security", "u:object_r:device_logging_prop:s0", "string", false},
+ {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+ {"logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+ {"persist.log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+ {"persist.mmc.", "u:object_r:mmc_prop:s0", "string", false},
+ {"persist.netd.stable_secret", "u:object_r:netd_stable_secret_prop:s0", "string", false},
+ {"persist.sys.", "u:object_r:system_prop:s0", "string", false},
+ {"persist.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+ {"ro.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+ {"persist.sys.audit_safemode", "u:object_r:safemode_prop:s0", "string", false},
+ {"persist.service.", "u:object_r:system_prop:s0", "string", false},
+ {"persist.service.bdroid.", "u:object_r:bluetooth_prop:s0", "string", false},
+ {"persist.security.", "u:object_r:system_prop:s0", "string", false},
+ {"persist.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+ {"ro.boot.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+ {"ro.boottime.", "u:object_r:boottime_prop:s0", "string", false},
+ {"ro.serialno", "u:object_r:serialno_prop:s0", "string", false},
+ {"ro.boot.btmacaddr", "u:object_r:bluetooth_prop:s0", "string", false},
+ {"ro.boot.serialno", "u:object_r:serialno_prop:s0", "string", false},
+ {"ro.bt.", "u:object_r:bluetooth_prop:s0", "string", false},
+ {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
+ {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
+ {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
+ {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+
+ {"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
+
+ {"vold.", "u:object_r:vold_prop:s0", "string", false},
+ {"ro.crypto.", "u:object_r:vold_prop:s0", "string", false},
+
+ {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0", "string", false},
+
+ {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0", "string",
+ false},
+
+ {"ctl.bootanim", "u:object_r:ctl_bootanim_prop:s0", "string", false},
+ {"ctl.dumpstate", "u:object_r:ctl_dumpstate_prop:s0", "string", false},
+ {"ctl.fuse_", "u:object_r:ctl_fuse_prop:s0", "string", false},
+ {"ctl.mdnsd", "u:object_r:ctl_mdnsd_prop:s0", "string", false},
+ {"ctl.ril-daemon", "u:object_r:ctl_rildaemon_prop:s0", "string", false},
+ {"ctl.bugreport", "u:object_r:ctl_bugreport_prop:s0", "string", false},
+ {"ctl.console", "u:object_r:ctl_console_prop:s0", "string", false},
+ {"ctl.", "u:object_r:ctl_default_prop:s0", "string", false},
+
+ {"nfc.", "u:object_r:nfc_prop:s0", "string", false},
+
+ {"config.", "u:object_r:config_prop:s0", "string", false},
+ {"ro.config.", "u:object_r:config_prop:s0", "string", false},
+ {"dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+ {"ro.dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+
+ {"wlan.", "u:object_r:wifi_prop:s0", "string", false},
+
+ {"lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+ {"ro.lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+
+ {"hwservicemanager.", "u:object_r:hwservicemanager_prop:s0", "string", false},
+ // Contexts from device/lge/bullhead/sepolicy/property_contexts
+ {"wc_transport.", "u:object_r:wc_transport_prop:s0", "string", false},
+ {"sys.listeners.", "u:object_r:qseecomtee_prop:s0", "string", false},
+ {"sys.keymaster.", "u:object_r:qseecomtee_prop:s0", "string", false},
+ {"radio.atfwd.", "u:object_r:radio_atfwd_prop:s0", "string", false},
+ {"sys.ims.", "u:object_r:qcom_ims_prop:s0", "string", false},
+ {"sensors.contexthub.", "u:object_r:contexthub_prop:s0", "string", false},
+ {"net.r_rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+ };
+
+ auto serialized_trie = std::string();
+ auto build_trie_error = std::string();
+ ASSERT_TRUE(BuildTrie(property_info, "u:object_r:default_prop:s0", "string", &serialized_trie,
+ &build_trie_error))
+ << build_trie_error;
+
+ auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+ auto properties_and_contexts = std::vector<std::pair<std::string, std::string>>{
+ // Actual properties on bullhead via `getprop -Z`
+ {"af.fast_track_multiplier", "u:object_r:default_prop:s0"},
+ {"audio_hal.period_size", "u:object_r:default_prop:s0"},
+ {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
+ {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heapmaxfree", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heapminfree", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heapsize", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heapstartsize", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.isa.arm64.features", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.isa.arm64.variant", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.lockprof.threshold", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.stack-trace-file", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.usejit", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.usejitprofiles", "u:object_r:dalvik_prop:s0"},
+ {"debug.atrace.tags.enableflags", "u:object_r:debug_prop:s0"},
+ {"debug.force_rtl", "u:object_r:debug_prop:s0"},
+ {"dev.bootcomplete", "u:object_r:system_prop:s0"},
+ {"drm.service.enabled", "u:object_r:default_prop:s0"},
+ {"gsm.current.phone-type", "u:object_r:radio_prop:s0"},
+ {"gsm.network.type", "u:object_r:radio_prop:s0"},
+ {"gsm.operator.alpha", "u:object_r:radio_prop:s0"},
+ {"gsm.operator.iso-country", "u:object_r:radio_prop:s0"},
+ {"gsm.operator.isroaming", "u:object_r:radio_prop:s0"},
+ {"gsm.operator.numeric", "u:object_r:radio_prop:s0"},
+ {"gsm.sim.operator.alpha", "u:object_r:radio_prop:s0"},
+ {"gsm.sim.operator.iso-country", "u:object_r:radio_prop:s0"},
+ {"gsm.sim.operator.numeric", "u:object_r:radio_prop:s0"},
+ {"gsm.sim.state", "u:object_r:radio_prop:s0"},
+ {"gsm.version.baseband", "u:object_r:radio_prop:s0"},
+ {"gsm.version.ril-impl", "u:object_r:radio_prop:s0"},
+ {"hwservicemanager.ready", "u:object_r:hwservicemanager_prop:s0"},
+ {"init.svc.adbd", "u:object_r:default_prop:s0"},
+ {"init.svc.atfwd", "u:object_r:default_prop:s0"},
+ {"init.svc.audioserver", "u:object_r:default_prop:s0"},
+ {"init.svc.bootanim", "u:object_r:default_prop:s0"},
+ {"init.svc.bullhead-sh", "u:object_r:default_prop:s0"},
+ {"init.svc.cameraserver", "u:object_r:default_prop:s0"},
+ {"init.svc.cnd", "u:object_r:default_prop:s0"},
+ {"init.svc.cnss-daemon", "u:object_r:default_prop:s0"},
+ {"init.svc.cnss_diag", "u:object_r:default_prop:s0"},
+ {"init.svc.configstore-hal-1-0", "u:object_r:default_prop:s0"},
+ {"init.svc.console", "u:object_r:default_prop:s0"},
+ {"init.svc.devstart_sh", "u:object_r:default_prop:s0"},
+ {"init.svc.drm", "u:object_r:default_prop:s0"},
+ {"init.svc.dumpstate-1-0", "u:object_r:default_prop:s0"},
+ {"init.svc.flash-nanohub-fw", "u:object_r:default_prop:s0"},
+ {"init.svc.fps_hal", "u:object_r:default_prop:s0"},
+ {"init.svc.gatekeeperd", "u:object_r:default_prop:s0"},
+ {"init.svc.gralloc-2-0", "u:object_r:default_prop:s0"},
+ {"init.svc.healthd", "u:object_r:default_prop:s0"},
+ {"init.svc.hidl_memory", "u:object_r:default_prop:s0"},
+ {"init.svc.hostapd", "u:object_r:default_prop:s0"},
+ {"init.svc.hwservicemanager", "u:object_r:default_prop:s0"},
+ {"init.svc.imsdatadaemon", "u:object_r:default_prop:s0"},
+ {"init.svc.imsqmidaemon", "u:object_r:default_prop:s0"},
+ {"init.svc.installd", "u:object_r:default_prop:s0"},
+ {"init.svc.irsc_util", "u:object_r:default_prop:s0"},
+ {"init.svc.keystore", "u:object_r:default_prop:s0"},
+ {"init.svc.lmkd", "u:object_r:default_prop:s0"},
+ {"init.svc.loc_launcher", "u:object_r:default_prop:s0"},
+ {"init.svc.logd", "u:object_r:default_prop:s0"},
+ {"init.svc.logd-reinit", "u:object_r:default_prop:s0"},
+ {"init.svc.media", "u:object_r:default_prop:s0"},
+ {"init.svc.mediadrm", "u:object_r:default_prop:s0"},
+ {"init.svc.mediaextractor", "u:object_r:default_prop:s0"},
+ {"init.svc.mediametrics", "u:object_r:default_prop:s0"},
+ {"init.svc.msm_irqbalance", "u:object_r:default_prop:s0"},
+ {"init.svc.netd", "u:object_r:default_prop:s0"},
+ {"init.svc.netmgrd", "u:object_r:default_prop:s0"},
+ {"init.svc.per_mgr", "u:object_r:default_prop:s0"},
+ {"init.svc.per_proxy", "u:object_r:default_prop:s0"},
+ {"init.svc.perfd", "u:object_r:default_prop:s0"},
+ {"init.svc.qcamerasvr", "u:object_r:default_prop:s0"},
+ {"init.svc.qmuxd", "u:object_r:default_prop:s0"},
+ {"init.svc.qseecomd", "u:object_r:default_prop:s0"},
+ {"init.svc.qti", "u:object_r:default_prop:s0"},
+ {"init.svc.ril-daemon", "u:object_r:default_prop:s0"},
+ {"init.svc.rmt_storage", "u:object_r:default_prop:s0"},
+ {"init.svc.servicemanager", "u:object_r:default_prop:s0"},
+ {"init.svc.ss_ramdump", "u:object_r:default_prop:s0"},
+ {"init.svc.start_hci_filter", "u:object_r:default_prop:s0"},
+ {"init.svc.storaged", "u:object_r:default_prop:s0"},
+ {"init.svc.surfaceflinger", "u:object_r:default_prop:s0"},
+ {"init.svc.thermal-engine", "u:object_r:default_prop:s0"},
+ {"init.svc.time_daemon", "u:object_r:default_prop:s0"},
+ {"init.svc.tombstoned", "u:object_r:default_prop:s0"},
+ {"init.svc.ueventd", "u:object_r:default_prop:s0"},
+ {"init.svc.update_engine", "u:object_r:default_prop:s0"},
+ {"init.svc.usb-hal-1-0", "u:object_r:default_prop:s0"},
+ {"init.svc.vndservicemanager", "u:object_r:default_prop:s0"},
+ {"init.svc.vold", "u:object_r:default_prop:s0"},
+ {"init.svc.webview_zygote32", "u:object_r:default_prop:s0"},
+ {"init.svc.wifi_hal_legacy", "u:object_r:default_prop:s0"},
+ {"init.svc.wificond", "u:object_r:default_prop:s0"},
+ {"init.svc.wpa_supplicant", "u:object_r:default_prop:s0"},
+ {"init.svc.zygote", "u:object_r:default_prop:s0"},
+ {"init.svc.zygote_secondary", "u:object_r:default_prop:s0"},
+ {"keyguard.no_require_sim", "u:object_r:default_prop:s0"},
+ {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0"},
+ {"logd.logpersistd.enable", "u:object_r:logpersistd_logging_prop:s0"},
+ {"media.aac_51_output_enabled", "u:object_r:default_prop:s0"},
+ {"media.recorder.show_manufacturer_and_model", "u:object_r:default_prop:s0"},
+ {"net.bt.name", "u:object_r:system_prop:s0"},
+ {"net.lte.ims.data.enabled", "u:object_r:net_radio_prop:s0"},
+ {"net.qtaguid_enabled", "u:object_r:system_prop:s0"},
+ {"net.tcp.default_init_rwnd", "u:object_r:system_prop:s0"},
+ {"nfc.initialized", "u:object_r:nfc_prop:s0"},
+ {"persist.audio.fluence.speaker", "u:object_r:audio_prop:s0"},
+ {"persist.audio.fluence.voicecall", "u:object_r:audio_prop:s0"},
+ {"persist.audio.fluence.voicecomm", "u:object_r:audio_prop:s0"},
+ {"persist.audio.fluence.voicerec", "u:object_r:audio_prop:s0"},
+ {"persist.camera.tnr.preview", "u:object_r:default_prop:s0"},
+ {"persist.camera.tnr.video", "u:object_r:default_prop:s0"},
+ {"persist.data.iwlan.enable", "u:object_r:default_prop:s0"},
+ {"persist.hwc.mdpcomp.enable", "u:object_r:default_prop:s0"},
+ {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0"},
+ {"persist.media.treble_omx", "u:object_r:default_prop:s0"},
+ {"persist.qcril.disable_retry", "u:object_r:default_prop:s0"},
+ {"persist.radio.adb_log_on", "u:object_r:radio_prop:s0"},
+ {"persist.radio.always_send_plmn", "u:object_r:radio_prop:s0"},
+ {"persist.radio.apm_sim_not_pwdn", "u:object_r:radio_prop:s0"},
+ {"persist.radio.custom_ecc", "u:object_r:radio_prop:s0"},
+ {"persist.radio.data_con_rprt", "u:object_r:radio_prop:s0"},
+ {"persist.radio.data_no_toggle", "u:object_r:radio_prop:s0"},
+ {"persist.radio.eons.enabled", "u:object_r:radio_prop:s0"},
+ {"persist.radio.eri64_as_home", "u:object_r:radio_prop:s0"},
+ {"persist.radio.mode_pref_nv10", "u:object_r:radio_prop:s0"},
+ {"persist.radio.process_sups_ind", "u:object_r:radio_prop:s0"},
+ {"persist.radio.redir_party_num", "u:object_r:radio_prop:s0"},
+ {"persist.radio.ril_payload_on", "u:object_r:radio_prop:s0"},
+ {"persist.radio.snapshot_enabled", "u:object_r:radio_prop:s0"},
+ {"persist.radio.snapshot_timer", "u:object_r:radio_prop:s0"},
+ {"persist.radio.use_cc_names", "u:object_r:radio_prop:s0"},
+ {"persist.speaker.prot.enable", "u:object_r:default_prop:s0"},
+ {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0"},
+ {"persist.sys.dalvik.vm.lib.2", "u:object_r:system_prop:s0"},
+ {"persist.sys.debug.color_temp", "u:object_r:system_prop:s0"},
+ {"persist.sys.preloads.file_cache_expired", "u:object_r:system_prop:s0"},
+ {"persist.sys.timezone", "u:object_r:system_prop:s0"},
+ {"persist.sys.usb.config", "u:object_r:system_prop:s0"},
+ {"persist.sys.webview.vmsize", "u:object_r:system_prop:s0"},
+ {"persist.tom", "u:object_r:default_prop:s0"},
+ {"persist.tom2", "u:object_r:default_prop:s0"},
+ {"pm.dexopt.ab-ota", "u:object_r:default_prop:s0"},
+ {"pm.dexopt.bg-dexopt", "u:object_r:default_prop:s0"},
+ {"pm.dexopt.boot", "u:object_r:default_prop:s0"},
+ {"pm.dexopt.first-boot", "u:object_r:default_prop:s0"},
+ {"pm.dexopt.install", "u:object_r:default_prop:s0"},
+ {"qcom.bluetooth.soc", "u:object_r:default_prop:s0"},
+ {"radio.atfwd.start", "u:object_r:radio_atfwd_prop:s0"},
+ {"ril.ecclist", "u:object_r:radio_prop:s0"},
+ {"ril.nosim.ecc_list_1", "u:object_r:radio_prop:s0"},
+ {"ril.nosim.ecc_list_count", "u:object_r:radio_prop:s0"},
+ {"ril.qcril_pre_init_lock_held", "u:object_r:radio_prop:s0"},
+ {"rild.libpath", "u:object_r:default_prop:s0"},
+ {"ro.allow.mock.location", "u:object_r:default_prop:s0"},
+ {"ro.audio.flinger_standbytime_ms", "u:object_r:default_prop:s0"},
+ {"ro.baseband", "u:object_r:default_prop:s0"},
+ {"ro.bionic.ld.warning", "u:object_r:default_prop:s0"},
+ {"ro.board.platform", "u:object_r:default_prop:s0"},
+ {"ro.boot.baseband", "u:object_r:default_prop:s0"},
+ {"ro.boot.bootloader", "u:object_r:default_prop:s0"},
+ {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0"},
+ {"ro.boot.dlcomplete", "u:object_r:default_prop:s0"},
+ {"ro.boot.emmc", "u:object_r:default_prop:s0"},
+ {"ro.boot.flash.locked", "u:object_r:default_prop:s0"},
+ {"ro.boot.hardware", "u:object_r:default_prop:s0"},
+ {"ro.boot.hardware.sku", "u:object_r:default_prop:s0"},
+ {"ro.boot.revision", "u:object_r:default_prop:s0"},
+ {"ro.boot.serialno", "u:object_r:serialno_prop:s0"},
+ {"ro.boot.verifiedbootstate", "u:object_r:default_prop:s0"},
+ {"ro.boot.veritymode", "u:object_r:default_prop:s0"},
+ {"ro.boot.wificountrycode", "u:object_r:default_prop:s0"},
+ {"ro.bootimage.build.date", "u:object_r:default_prop:s0"},
+ {"ro.bootimage.build.date.utc", "u:object_r:default_prop:s0"},
+ {"ro.bootimage.build.fingerprint", "u:object_r:default_prop:s0"},
+ {"ro.bootloader", "u:object_r:default_prop:s0"},
+ {"ro.bootmode", "u:object_r:default_prop:s0"},
+ {"ro.boottime.adbd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.atfwd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.audioserver", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.bootanim", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.bullhead-sh", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.cameraserver", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.cnd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.cnss-daemon", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.cnss_diag", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.configstore-hal-1-0", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.console", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.devstart_sh", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.drm", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.dumpstate-1-0", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.flash-nanohub-fw", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.fps_hal", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.gatekeeperd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.gralloc-2-0", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.healthd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.hidl_memory", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.hwservicemanager", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.installd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.irsc_util", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.keystore", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.lmkd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.loc_launcher", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.logd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.logd-reinit", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.media", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.mediadrm", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.mediaextractor", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.mediametrics", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.msm_irqbalance", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.netd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.netmgrd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.per_mgr", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.per_proxy", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.perfd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.qcamerasvr", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.qmuxd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.qseecomd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.qti", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.ril-daemon", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.rmt_storage", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.servicemanager", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.ss_ramdump", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.start_hci_filter", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.storaged", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.surfaceflinger", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.thermal-engine", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.time_daemon", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.tombstoned", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.ueventd", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.update_engine", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.usb-hal-1-0", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.vndservicemanager", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.vold", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.webview_zygote32", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.wifi_hal_legacy", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.wificond", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.zygote", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.zygote_secondary", "u:object_r:boottime_prop:s0"},
+ {"ro.bt.bdaddr_path", "u:object_r:bluetooth_prop:s0"},
+ {"ro.build.characteristics", "u:object_r:default_prop:s0"},
+ {"ro.build.date", "u:object_r:default_prop:s0"},
+ {"ro.build.date.utc", "u:object_r:default_prop:s0"},
+ {"ro.build.description", "u:object_r:default_prop:s0"},
+ {"ro.build.display.id", "u:object_r:default_prop:s0"},
+ {"ro.build.expect.baseband", "u:object_r:default_prop:s0"},
+ {"ro.build.expect.bootloader", "u:object_r:default_prop:s0"},
+ {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0"},
+ {"ro.build.flavor", "u:object_r:default_prop:s0"},
+ {"ro.build.host", "u:object_r:default_prop:s0"},
+ {"ro.build.id", "u:object_r:default_prop:s0"},
+ {"ro.build.product", "u:object_r:default_prop:s0"},
+ {"ro.build.tags", "u:object_r:default_prop:s0"},
+ {"ro.build.type", "u:object_r:default_prop:s0"},
+ {"ro.build.user", "u:object_r:default_prop:s0"},
+ {"ro.build.version.all_codenames", "u:object_r:default_prop:s0"},
+ {"ro.build.version.base_os", "u:object_r:default_prop:s0"},
+ {"ro.build.version.codename", "u:object_r:default_prop:s0"},
+ {"ro.build.version.incremental", "u:object_r:default_prop:s0"},
+ {"ro.build.version.preview_sdk", "u:object_r:default_prop:s0"},
+ {"ro.build.version.release", "u:object_r:default_prop:s0"},
+ {"ro.build.version.sdk", "u:object_r:default_prop:s0"},
+ {"ro.build.version.security_patch", "u:object_r:default_prop:s0"},
+ {"ro.camera.notify_nfc", "u:object_r:default_prop:s0"},
+ {"ro.carrier", "u:object_r:default_prop:s0"},
+ {"ro.com.android.dataroaming", "u:object_r:default_prop:s0"},
+ {"ro.config.alarm_alert", "u:object_r:config_prop:s0"},
+ {"ro.config.notification_sound", "u:object_r:config_prop:s0"},
+ {"ro.config.ringtone", "u:object_r:config_prop:s0"},
+ {"ro.config.vc_call_vol_steps", "u:object_r:config_prop:s0"},
+ {"ro.crypto.fs_crypto_blkdev", "u:object_r:vold_prop:s0"},
+ {"ro.crypto.state", "u:object_r:vold_prop:s0"},
+ {"ro.crypto.type", "u:object_r:vold_prop:s0"},
+ {"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
+ {"ro.debuggable", "u:object_r:default_prop:s0"},
+ {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+ {"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
+ {"ro.frp.pst", "u:object_r:default_prop:s0"},
+ {"ro.hardware", "u:object_r:default_prop:s0"},
+ {"ro.hwui.drop_shadow_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.hwui.gradient_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.hwui.layer_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.hwui.path_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.hwui.r_buffer_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.hwui.text_large_cache_height", "u:object_r:default_prop:s0"},
+ {"ro.hwui.text_large_cache_width", "u:object_r:default_prop:s0"},
+ {"ro.hwui.text_small_cache_height", "u:object_r:default_prop:s0"},
+ {"ro.hwui.text_small_cache_width", "u:object_r:default_prop:s0"},
+ {"ro.hwui.texture_cache_flushrate", "u:object_r:default_prop:s0"},
+ {"ro.hwui.texture_cache_size", "u:object_r:default_prop:s0"},
+ {"ro.min_freq_0", "u:object_r:default_prop:s0"},
+ {"ro.min_freq_4", "u:object_r:default_prop:s0"},
+ {"ro.oem_unlock_supported", "u:object_r:default_prop:s0"},
+ {"ro.opengles.version", "u:object_r:default_prop:s0"},
+ {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0"},
+ {"ro.product.board", "u:object_r:default_prop:s0"},
+ {"ro.product.brand", "u:object_r:default_prop:s0"},
+ {"ro.product.cpu.abi", "u:object_r:default_prop:s0"},
+ {"ro.product.cpu.abilist", "u:object_r:default_prop:s0"},
+ {"ro.product.cpu.abilist32", "u:object_r:default_prop:s0"},
+ {"ro.product.cpu.abilist64", "u:object_r:default_prop:s0"},
+ {"ro.product.device", "u:object_r:default_prop:s0"},
+ {"ro.product.first_api_level", "u:object_r:default_prop:s0"},
+ {"ro.product.locale", "u:object_r:default_prop:s0"},
+ {"ro.product.manufacturer", "u:object_r:default_prop:s0"},
+ {"ro.product.model", "u:object_r:default_prop:s0"},
+ {"ro.product.name", "u:object_r:default_prop:s0"},
+ {"ro.property_service.version", "u:object_r:default_prop:s0"},
+ {"ro.qc.sdk.audio.fluencetype", "u:object_r:default_prop:s0"},
+ {"ro.recovery_id", "u:object_r:default_prop:s0"},
+ {"ro.revision", "u:object_r:default_prop:s0"},
+ {"ro.ril.svdo", "u:object_r:radio_prop:s0"},
+ {"ro.ril.svlte1x", "u:object_r:radio_prop:s0"},
+ {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0"},
+ {"ro.secure", "u:object_r:default_prop:s0"},
+ {"ro.serialno", "u:object_r:serialno_prop:s0"},
+ {"ro.sf.lcd_density", "u:object_r:default_prop:s0"},
+ {"ro.telephony.call_ring.multiple", "u:object_r:default_prop:s0"},
+ {"ro.telephony.default_cdma_sub", "u:object_r:default_prop:s0"},
+ {"ro.telephony.default_network", "u:object_r:default_prop:s0"},
+ {"ro.treble.enabled", "u:object_r:default_prop:s0"},
+ {"ro.vendor.build.date", "u:object_r:default_prop:s0"},
+ {"ro.vendor.build.date.utc", "u:object_r:default_prop:s0"},
+ {"ro.vendor.build.fingerprint", "u:object_r:default_prop:s0"},
+ {"ro.vendor.extension_library", "u:object_r:default_prop:s0"},
+ {"ro.wifi.channels", "u:object_r:default_prop:s0"},
+ {"ro.zygote", "u:object_r:default_prop:s0"},
+ {"security.perf_harden", "u:object_r:shell_prop:s0"},
+ {"sensors.contexthub.lid_state", "u:object_r:contexthub_prop:s0"},
+ {"service.adb.root", "u:object_r:shell_prop:s0"},
+ {"service.bootanim.exit", "u:object_r:system_prop:s0"},
+ {"service.sf.present_timestamp", "u:object_r:system_prop:s0"},
+ {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0"},
+ {"sys.boot_completed", "u:object_r:system_prop:s0"},
+ {"sys.ims.QMI_DAEMON_STATUS", "u:object_r:qcom_ims_prop:s0"},
+ {"sys.listeners.registered", "u:object_r:qseecomtee_prop:s0"},
+ {"sys.logbootcomplete", "u:object_r:system_prop:s0"},
+ {"sys.oem_unlock_allowed", "u:object_r:system_prop:s0"},
+ {"sys.qcom.devup", "u:object_r:system_prop:s0"},
+ {"sys.sysctl.extra_free_kbytes", "u:object_r:system_prop:s0"},
+ {"sys.usb.config", "u:object_r:system_radio_prop:s0"},
+ {"sys.usb.configfs", "u:object_r:system_radio_prop:s0"},
+ {"sys.usb.controller", "u:object_r:system_prop:s0"},
+ {"sys.usb.ffs.aio_compat", "u:object_r:ffs_prop:s0"},
+ {"sys.usb.ffs.max_read", "u:object_r:ffs_prop:s0"},
+ {"sys.usb.ffs.max_write", "u:object_r:ffs_prop:s0"},
+ {"sys.usb.ffs.ready", "u:object_r:ffs_prop:s0"},
+ {"sys.usb.mtp.device_type", "u:object_r:system_prop:s0"},
+ {"sys.usb.state", "u:object_r:system_prop:s0"},
+ {"telephony.lteOnCdmaDevice", "u:object_r:default_prop:s0"},
+ {"tombstoned.max_tombstone_count", "u:object_r:default_prop:s0"},
+ {"vidc.debug.perf.mode", "u:object_r:default_prop:s0"},
+ {"vidc.enc.dcvs.extra-buff-count", "u:object_r:default_prop:s0"},
+ {"vold.decrypt", "u:object_r:vold_prop:s0"},
+ {"vold.has_adoptable", "u:object_r:vold_prop:s0"},
+ {"vold.post_fs_data_done", "u:object_r:vold_prop:s0"},
+ {"wc_transport.clean_up", "u:object_r:wc_transport_prop:s0"},
+ {"wc_transport.hci_filter_status", "u:object_r:wc_transport_prop:s0"},
+ {"wc_transport.ref_count", "u:object_r:wc_transport_prop:s0"},
+ {"wc_transport.soc_initialized", "u:object_r:wc_transport_prop:s0"},
+ {"wc_transport.start_hci", "u:object_r:wc_transport_prop:s0"},
+ {"wc_transport.vnd_power", "u:object_r:wc_transport_prop:s0"},
+ {"wifi.interface", "u:object_r:default_prop:s0"},
+ {"wifi.supplicant_scan_interval", "u:object_r:default_prop:s0"},
+ };
+
+ for (const auto& [property, context] : properties_and_contexts) {
+ const char* returned_context;
+ property_info_area->GetPropertyInfo(property.c_str(), &returned_context, nullptr);
+ EXPECT_EQ(context, returned_context) << property;
+ }
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_without_dot) {
+ auto property_info = std::vector<PropertyInfoEntry>{
+ {"persist.radio", "1st", "1st", false},
+ {"persist.radio.something.else.here", "2nd", "2nd", false},
+ };
+
+ auto serialized_trie = std::string();
+ auto build_trie_error = std::string();
+ ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+ << build_trie_error;
+
+ auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+ const char* context;
+ const char* schema;
+ property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context,
+ &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {
+ auto property_info = std::vector<PropertyInfoEntry>{
+ {"persist.", "1st", "1st", false},
+ {"persist.radio", "2nd", "2nd", false},
+ {"persist.radio.long.property.exact.match", "3rd", "3rd", true},
+ };
+
+ auto serialized_trie = std::string();
+ auto build_trie_error = std::string();
+ ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+ << build_trie_error;
+
+ auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+ const char* context;
+ const char* schema;
+ property_info_area->GetPropertyInfo("persist.notradio", &context, &schema);
+ EXPECT_STREQ("1st", context);
+ EXPECT_STREQ("1st", schema);
+ property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &schema);
+ EXPECT_STREQ("2nd", context);
+ EXPECT_STREQ("2nd", schema);
+ property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &schema);
+ EXPECT_STREQ("3rd", context);
+ EXPECT_STREQ("3rd", schema);
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp
new file mode 100644
index 0000000..feb753b
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "trie_builder.h"
+
+#include <android-base/strings.h>
+
+using android::base::Split;
+
+namespace android {
+namespace properties {
+
+TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_schema)
+ : builder_root_("root") {
+ auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
+ builder_root_.set_context(context_pointer);
+ auto* schema_pointer = StringPointerFromContainer(default_schema, &schemas_);
+ builder_root_.set_schema(schema_pointer);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
+ const std::string& schema, bool exact, std::string* error) {
+ auto* context_pointer = StringPointerFromContainer(context, &contexts_);
+ auto* schema_pointer = StringPointerFromContainer(schema, &schemas_);
+ return AddToTrie(name, context_pointer, schema_pointer, exact, error);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
+ const std::string* schema, bool exact, std::string* error) {
+ TrieBuilderNode* current_node = &builder_root_;
+
+ auto name_pieces = Split(name, ".");
+
+ bool ends_with_dot = false;
+ if (name_pieces.back().empty()) {
+ ends_with_dot = true;
+ name_pieces.pop_back();
+ }
+
+ // Move us to the final node that we care about, adding incremental nodes if necessary.
+ while (name_pieces.size() > 1) {
+ auto child = current_node->FindChild(name_pieces.front());
+ if (child == nullptr) {
+ child = current_node->AddChild(name_pieces.front());
+ }
+ if (child == nullptr) {
+ *error = "Unable to allocate Trie node";
+ return false;
+ }
+ current_node = child;
+ name_pieces.erase(name_pieces.begin());
+ }
+
+ // Store our context based on what type of match it is.
+ if (exact) {
+ if (!current_node->AddExactMatchContext(name_pieces.front(), context, schema)) {
+ *error = "Duplicate exact match detected for '" + name + "'";
+ return false;
+ }
+ } else if (!ends_with_dot) {
+ if (!current_node->AddPrefixContext(name_pieces.front(), context, schema)) {
+ *error = "Duplicate prefix match detected for '" + name + "'";
+ return false;
+ }
+ } else {
+ auto child = current_node->FindChild(name_pieces.front());
+ if (child == nullptr) {
+ child = current_node->AddChild(name_pieces.front());
+ }
+ if (child == nullptr) {
+ *error = "Unable to allocate Trie node";
+ return false;
+ }
+ if (child->context() != nullptr || child->schema() != nullptr) {
+ *error = "Duplicate prefix match detected for '" + name + "'";
+ return false;
+ }
+ child->set_context(context);
+ child->set_schema(schema);
+ }
+ return true;
+}
+
+const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
+ std::set<std::string>* container) {
+ // Get a pointer to the string in a given set, such that we only ever serialize each string once.
+ auto [iterator, _] = container->emplace(string);
+ return &(*iterator);
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h
new file mode 100644
index 0000000..f928e76
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.h
@@ -0,0 +1,124 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyEntryBuilder {
+ PropertyEntryBuilder() : context(nullptr), schema(nullptr) {}
+ PropertyEntryBuilder(const std::string& name, const std::string* context,
+ const std::string* schema)
+ : name(name), context(context), schema(schema) {}
+ std::string name;
+ const std::string* context;
+ const std::string* schema;
+};
+
+class TrieBuilderNode {
+ public:
+ TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}
+
+ TrieBuilderNode* FindChild(const std::string& name) {
+ for (auto& child : children_) {
+ if (child.name() == name) return &child;
+ }
+ return nullptr;
+ }
+
+ const TrieBuilderNode* FindChild(const std::string& name) const {
+ for (const auto& child : children_) {
+ if (child.name() == name) return &child;
+ }
+ return nullptr;
+ }
+
+ TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
+
+ bool AddPrefixContext(const std::string& prefix, const std::string* context,
+ const std::string* schema) {
+ if (std::find_if(prefixes_.begin(), prefixes_.end(),
+ [&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
+ return false;
+ }
+
+ prefixes_.emplace_back(prefix, context, schema);
+ return true;
+ }
+
+ bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
+ const std::string* schema) {
+ if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
+ return t.name == exact_match;
+ }) != exact_matches_.end()) {
+ return false;
+ }
+
+ exact_matches_.emplace_back(exact_match, context, schema);
+ return true;
+ }
+
+ const std::string& name() const { return property_entry_.name; }
+ const std::string* context() const { return property_entry_.context; }
+ void set_context(const std::string* context) { property_entry_.context = context; }
+ const std::string* schema() const { return property_entry_.schema; }
+ void set_schema(const std::string* schema) { property_entry_.schema = schema; }
+
+ const PropertyEntryBuilder property_entry() const { return property_entry_; }
+
+ const std::vector<TrieBuilderNode>& children() const { return children_; }
+ const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }
+ const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }
+
+ private:
+ PropertyEntryBuilder property_entry_;
+ std::vector<TrieBuilderNode> children_;
+ std::vector<PropertyEntryBuilder> prefixes_;
+ std::vector<PropertyEntryBuilder> exact_matches_;
+};
+
+class TrieBuilder {
+ public:
+ TrieBuilder(const std::string& default_context, const std::string& default_schema);
+ bool AddToTrie(const std::string& name, const std::string& context, const std::string& schema,
+ bool exact, std::string* error);
+
+ const TrieBuilderNode builder_root() const { return builder_root_; }
+ const std::set<std::string>& contexts() const { return contexts_; }
+ const std::set<std::string>& schemas() const { return schemas_; }
+
+ private:
+ bool AddToTrie(const std::string& name, const std::string* context, const std::string* schema,
+ bool exact, std::string* error);
+ const std::string* StringPointerFromContainer(const std::string& string,
+ std::set<std::string>* container);
+
+ TrieBuilderNode builder_root_;
+ std::set<std::string> contexts_;
+ std::set<std::string> schemas_;
+};
+
+} // namespace properties
+} // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
new file mode 100644
index 0000000..2b948f3
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "trie_builder.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, BuildTrie_Simple) {
+ auto trie_builder = TrieBuilder("default", "default_schema");
+
+ // Add test data to tree
+ auto error = std::string();
+ EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_schema", false, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_schema", false, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_schema", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_schema", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_schema", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_schema", true, &error));
+
+ ASSERT_EQ(5U, trie_builder.contexts().size());
+ ASSERT_EQ(5U, trie_builder.schemas().size());
+
+ auto& builder_root = trie_builder.builder_root();
+
+ // Check the root node
+ EXPECT_EQ("root", builder_root.name());
+ ASSERT_NE(nullptr, builder_root.context());
+ EXPECT_EQ("default", *builder_root.context());
+ ASSERT_NE(nullptr, builder_root.schema());
+ EXPECT_EQ("default_schema", *builder_root.schema());
+
+ EXPECT_EQ(0U, builder_root.prefixes().size());
+ EXPECT_EQ(0U, builder_root.exact_matches().size());
+
+ ASSERT_EQ(2U, builder_root.children().size());
+
+ // Check the 'test.' node
+ auto* test_node = builder_root.FindChild("test");
+ EXPECT_EQ("test", test_node->name());
+ ASSERT_NE(nullptr, test_node->context());
+ EXPECT_EQ("1st", *test_node->context());
+ ASSERT_NE(nullptr, test_node->schema());
+ EXPECT_EQ("1st_schema", *test_node->schema());
+
+ EXPECT_EQ(0U, test_node->children().size());
+ EXPECT_EQ(1U, test_node->prefixes().size());
+ {
+ auto& property_entry = test_node->prefixes()[0];
+ EXPECT_EQ("test", property_entry.name);
+ ASSERT_NE(nullptr, property_entry.context);
+ EXPECT_EQ("2nd", *property_entry.context);
+ ASSERT_NE(nullptr, property_entry.schema);
+ EXPECT_EQ("2nd_schema", *property_entry.schema);
+ }
+ EXPECT_EQ(3U, test_node->exact_matches().size());
+ EXPECT_EQ("test1", test_node->exact_matches()[0].name);
+ EXPECT_EQ("test2", test_node->exact_matches()[1].name);
+ EXPECT_EQ("test3", test_node->exact_matches()[2].name);
+
+ ASSERT_NE(nullptr, test_node->exact_matches()[0].context);
+ ASSERT_NE(nullptr, test_node->exact_matches()[1].context);
+ ASSERT_NE(nullptr, test_node->exact_matches()[2].context);
+ EXPECT_EQ("3rd", *test_node->exact_matches()[0].context);
+ EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
+ EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
+
+ ASSERT_NE(nullptr, test_node->exact_matches()[0].schema);
+ ASSERT_NE(nullptr, test_node->exact_matches()[1].schema);
+ ASSERT_NE(nullptr, test_node->exact_matches()[2].schema);
+ EXPECT_EQ("3rd_schema", *test_node->exact_matches()[0].schema);
+ EXPECT_EQ("3rd_schema", *test_node->exact_matches()[1].schema);
+ EXPECT_EQ("3rd_schema", *test_node->exact_matches()[2].schema);
+
+ // Check the long string node
+ auto expect_empty_one_child = [](auto* node) {
+ ASSERT_NE(nullptr, node);
+ EXPECT_EQ(nullptr, node->context());
+ EXPECT_EQ(nullptr, node->schema());
+ EXPECT_EQ(0U, node->prefixes().size());
+ EXPECT_EQ(0U, node->exact_matches().size());
+ EXPECT_EQ(1U, node->children().size());
+ };
+
+ // Start with 'this'
+ auto* long_string_node = builder_root.FindChild("this");
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'is'
+ long_string_node = long_string_node->FindChild("is");
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'a'
+ long_string_node = long_string_node->FindChild("a");
+ expect_empty_one_child(long_string_node);
+
+ // Move to 'long'
+ long_string_node = long_string_node->FindChild("long");
+ EXPECT_EQ(0U, long_string_node->prefixes().size());
+ EXPECT_EQ(1U, long_string_node->exact_matches().size());
+ EXPECT_EQ(0U, long_string_node->children().size());
+
+ {
+ auto& property_entry = long_string_node->exact_matches()[0];
+ EXPECT_EQ("string", property_entry.name);
+ ASSERT_NE(nullptr, property_entry.context);
+ EXPECT_EQ("4th", *property_entry.context);
+ ASSERT_NE(nullptr, property_entry.schema);
+ EXPECT_EQ("4th_schema", *property_entry.schema);
+ }
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_node_arena.h b/property_service/libpropertyinfoserializer/trie_node_arena.h
new file mode 100644
index 0000000..5e0ef82
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_node_arena.h
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+template <typename T>
+class ArenaObjectPointer {
+ public:
+ ArenaObjectPointer(std::string& arena_data, uint32_t offset)
+ : arena_data_(arena_data), offset_(offset) {}
+
+ T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }
+
+ private:
+ std::string& arena_data_;
+ uint32_t offset_;
+};
+
+class TrieNodeArena {
+ public:
+ TrieNodeArena() : current_data_pointer_(0) {}
+
+ // We can't return pointers to objects since data_ may move when reallocated, thus invalidating
+ // any pointers. Therefore we return an ArenaObjectPointer, which always accesses elements via
+ // data_ + offset.
+ template <typename T>
+ ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
+ uint32_t offset;
+ AllocateData(sizeof(T), &offset);
+ if (return_offset) *return_offset = offset;
+ return ArenaObjectPointer<T>(data_, offset);
+ }
+
+ uint32_t AllocateUint32Array(int length) {
+ uint32_t offset;
+ AllocateData(sizeof(uint32_t) * length, &offset);
+ return offset;
+ }
+
+ uint32_t* uint32_array(uint32_t offset) {
+ return reinterpret_cast<uint32_t*>(data_.data() + offset);
+ }
+
+ uint32_t AllocateAndWriteString(const std::string& string) {
+ uint32_t offset;
+ char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));
+ strcpy(data, string.c_str());
+ return offset;
+ }
+
+ void AllocateAndWriteUint32(uint32_t value) {
+ auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
+ *location = value;
+ }
+
+ void* AllocateData(size_t size, uint32_t* offset) {
+ size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
+
+ if (current_data_pointer_ + aligned_size > data_.size()) {
+ auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
+ data_.resize(new_size, '\0');
+ }
+ if (offset) *offset = current_data_pointer_;
+
+ uint32_t return_offset = current_data_pointer_;
+ current_data_pointer_ += aligned_size;
+ return &data_[0] + return_offset;
+ }
+
+ uint32_t size() const { return current_data_pointer_; }
+
+ const std::string& data() const { return data_; }
+
+ std::string truncated_data() const {
+ auto result = data_;
+ result.resize(current_data_pointer_);
+ return result;
+ }
+
+ private:
+ std::string data_;
+ uint32_t current_data_pointer_;
+};
+
+} // namespace properties
+} // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp
new file mode 100644
index 0000000..5326537
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+// Serialized strings contains:
+// 1) A uint32_t count of elements in the below array
+// 2) A sorted array of uint32_t offsets pointing to null terminated strings
+// 3) Each of the null terminated strings themselves packed back to back
+// This returns the offset into arena where the serialized strings start.
+void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
+ arena_->AllocateAndWriteUint32(strings.size());
+
+ // Allocate space for the array.
+ uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
+
+ // Write offset pointers and strings; these are already alphabetically sorted by virtue of being
+ // in an std::set.
+ auto it = strings.begin();
+ for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
+ uint32_t string_offset = arena_->AllocateAndWriteString(*it);
+ arena_->uint32_array(offset_array_offset)[i] = string_offset;
+ }
+}
+
+uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
+ uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
+ ? serialized_info()->FindContextIndex(property_entry.context->c_str())
+ : ~0u;
+ uint32_t schema_index = property_entry.schema != nullptr && !property_entry.schema->empty()
+ ? serialized_info()->FindSchemaIndex(property_entry.schema->c_str())
+ : ~0u;
+ uint32_t offset;
+ auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
+ serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
+ serialized_property_entry->namelen = property_entry.name.size();
+ serialized_property_entry->context_index = context_index;
+ serialized_property_entry->schema_index = schema_index;
+ return offset;
+}
+
+uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
+ uint32_t trie_offset;
+ auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
+
+ trie->property_entry = WritePropertyEntry(builder_node.property_entry());
+
+ // Write prefix matches
+ auto sorted_prefix_matches = builder_node.prefixes();
+ // Prefixes are sorted by descending length
+ std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
+
+ trie->num_prefixes = sorted_prefix_matches.size();
+
+ uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
+ trie->prefix_entries = prefix_entries_array_offset;
+
+ for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
+ uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
+ arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
+ }
+
+ // Write exact matches
+ auto sorted_exact_matches = builder_node.exact_matches();
+ // Exact matches are sorted alphabetically
+ std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
+
+ trie->num_exact_matches = sorted_exact_matches.size();
+
+ uint32_t exact_match_entries_array_offset =
+ arena_->AllocateUint32Array(sorted_exact_matches.size());
+ trie->exact_match_entries = exact_match_entries_array_offset;
+
+ for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
+ uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
+ arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
+ }
+
+ // Write children
+ auto sorted_children = builder_node.children();
+ std::sort(sorted_children.begin(), sorted_children.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
+
+ trie->num_child_nodes = sorted_children.size();
+ uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
+ trie->child_nodes = children_offset_array_offset;
+
+ for (unsigned int i = 0; i < sorted_children.size(); ++i) {
+ arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
+ }
+ return trie_offset;
+}
+
+TrieSerializer::TrieSerializer() {}
+
+std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
+ arena_.reset(new TrieNodeArena());
+
+ auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
+ header->current_version = 1;
+ header->minimum_supported_version = 1;
+
+ // Store where we're about to write the contexts.
+ header->contexts_offset = arena_->size();
+ SerializeStrings(trie_builder.contexts());
+
+ // Store where we're about to write the schemas.
+ header->schemas_offset = arena_->size();
+ SerializeStrings(trie_builder.schemas());
+
+ // We need to store size() up to this point now for Find*Offset() to work.
+ header->size = arena_->size();
+
+ uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
+ header->root_offset = root_trie_offset;
+
+ // Record the real size now that we've written everything
+ header->size = arena_->size();
+
+ return arena_->truncated_data();
+}
+
+} // namespace properties
+} // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.h b/property_service/libpropertyinfoserializer/trie_serializer.h
new file mode 100644
index 0000000..e4d3343
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+#include "property_info_parser/property_info_parser.h"
+
+#include "trie_builder.h"
+#include "trie_node_arena.h"
+
+namespace android {
+namespace properties {
+
+class TrieSerializer {
+ public:
+ TrieSerializer();
+
+ std::string SerializeTrie(const TrieBuilder& trie_builder);
+
+ private:
+ void SerializeStrings(const std::set<std::string>& strings);
+ uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);
+
+ // Writes a new TrieNode to arena, and recursively writes its children.
+ // Returns the offset within arena.
+ uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);
+
+ const PropertyInfoArea* serialized_info() const {
+ return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
+ }
+
+ std::unique_ptr<TrieNodeArena> arena_;
+};
+
+} // namespace properties
+} // namespace android
+
+#endif
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 777fdb0..492d63a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -36,70 +36,6 @@
include $(BUILD_PREBUILT)
-# Modules for asan.options.X files.
-
-ASAN_OPTIONS_FILES :=
-
-define create-asan-options-module
-include $$(CLEAR_VARS)
-LOCAL_MODULE := asan.options.$(1)
-ASAN_OPTIONS_FILES += asan.options.$(1)
-LOCAL_MODULE_CLASS := ETC
-# The asan.options.off.template tries to turn off as much of ASAN as is possible.
-LOCAL_SRC_FILES := asan.options.off.template
-LOCAL_MODULE_PATH := $(TARGET_OUT)
-include $$(BUILD_PREBUILT)
-endef
-
-# Pretty comprehensive set of native services. This list is helpful if all that's to be checked is an
-# app.
-ifeq ($(SANITIZE_LITE_SERVICES),true)
-SANITIZE_ASAN_OPTIONS_FOR := \
- adbd \
- ATFWD-daemon \
- audioserver \
- bridgemgrd \
- cameraserver \
- cnd \
- debuggerd \
- dex2oat \
- drmserver \
- fingerprintd \
- gatekeeperd \
- installd \
- keystore \
- lmkd \
- logcat \
- logd \
- lowi-server \
- media.codec \
- mediadrmserver \
- media.extractor \
- mediaserver \
- mm-qcamera-daemon \
- mpdecision \
- netmgrd \
- perfd \
- perfprofd \
- qmuxd \
- qseecomd \
- rild \
- sdcard \
- servicemanager \
- slim_daemon \
- surfaceflinger \
- thermal-engine \
- time_daemon \
- update_engine \
- vold \
- wpa_supplicant \
- zip
-endif
-
-ifneq ($(SANITIZE_ASAN_OPTIONS_FOR),)
- $(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
-endif
-
# ASAN extration.
ASAN_EXTRACT_FILES :=
ifeq ($(SANITIZE_TARGET_SYSTEM),true)
@@ -189,6 +125,18 @@
bcp_md5 :=
bcp_dep :=
+# If PLATFORM_VNDK_VERSION is defined and not "current", generate versioned
+# module names for ld.config.txt, llndk.libraries.txt and vndksp.libraries.txt
+# files.
+define versioned_module_name
+$(strip \
+ $(if $(filter-out current,$(PLATFORM_VNDK_VERSION)), \
+ $(basename $(LOCAL_MODULE)).$(PLATFORM_VNDK_VERSION)$(suffix $(LOCAL_MODULE)), \
+ $(LOCAL_MODULE) \
+ ) \
+)
+endef
+
#######################################
# ld.config.txt
include $(CLEAR_VARS)
@@ -205,22 +153,22 @@
LOCAL_MODULE := ld.config.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
include $(BUILD_SYSTEM)/base_rules.mk
-llndk_libraries := $(subst $(space),:,$(addsuffix .so,\
+llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
-private_llndk_libraries := $(subst $(space),:,$(addsuffix .so,\
+private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
-vndk_sameprocess_libraries := $(subst $(space),:,$(addsuffix .so,\
+vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
-vndk_core_libraries := $(subst $(space),:,$(addsuffix .so,\
+vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
-sanitizer_runtime_libraries := $(subst $(space),:,$(addsuffix .so,\
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
$(UBSAN_RUNTIME_LIBRARY) \
$(TSAN_RUNTIME_LIBRARY) \
@@ -251,13 +199,14 @@
LOCAL_MODULE := ld.config.txt
ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-LOCAL_SRC_FILES := etc/ld.config.txt
+ LOCAL_SRC_FILES := etc/ld.config.txt
+ LOCAL_MODULE_STEM := $(call versioned_module_name)
else
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+ LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+ LOCAL_MODULE_STEM := $(LOCAL_MODULE)
endif
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
endif
@@ -267,7 +216,7 @@
LOCAL_MODULE := llndk.libraries.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
$(LOCAL_BUILT_MODULE):
@@ -283,7 +232,7 @@
LOCAL_MODULE := vndksp.libraries.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
$(LOCAL_BUILT_MODULE):
diff --git a/rootdir/asan.options.off.template b/rootdir/asan.options.off.template
deleted file mode 100644
index 59a1249..0000000
--- a/rootdir/asan.options.off.template
+++ /dev/null
@@ -1,7 +0,0 @@
-quarantine_size_mb=0
-max_redzone=16
-poison_heap=false
-poison_partial=false
-poison_array_cookie=false
-alloc_dealloc_mismatch=false
-new_delete_type_mismatch=false
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 4d058db..d55ec57 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -12,5 +12,14 @@
[legacy]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}:/odm/${LIB}
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3183b80..60afdd7 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -3,9 +3,11 @@
# Bionic loader config file.
#
-# Don't change the order here.
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
dir.vendor = /data/nativetest/odm
@@ -16,6 +18,7 @@
dir.vendor = /data/nativetest64/vendor
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
+
dir.system = /data/nativetest
dir.system = /data/nativetest64
dir.system = /data/benchmarktest
@@ -27,25 +30,21 @@
###############################################################################
# "default" namespace
#
-# Framework-side code runs in this namespace. Anything from /vendor partition
-# can't be loaded in this namespace.
+# Framework-side code runs in this namespace. However, libs from other
+# partitions are also allowed temporarily.
###############################################################################
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
-namespace.default.permitted.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
-# TODO(b/37013858): remove all dependencies to /vendor/lib from system processes
-# When this is done, comment out following three lines and remove the three
-# lines above
-#namespace.default.isolated = true
-#namespace.default.search.paths = /system/${LIB}
-#namespace.default.permitted.paths = /system/${LIB}
-#
-#namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-#namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
###############################################################################
# "sphal" namespace
@@ -62,21 +61,56 @@
###############################################################################
namespace.sphal.isolated = true
namespace.sphal.visible = true
-namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
-namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.sphal.search.paths = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths += /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths += /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths += /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
# libs listed here can be used.
namespace.sphal.links = default,vndk,rs
# WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so:libz.so
+namespace.sphal.link.default.shared_libs = libc.so
+namespace.sphal.link.default.shared_libs += libEGL.so
+namespace.sphal.link.default.shared_libs += libGLESv1_CM.so
+namespace.sphal.link.default.shared_libs += libGLESv2.so
+namespace.sphal.link.default.shared_libs += libdl.so
+namespace.sphal.link.default.shared_libs += liblog.so
+namespace.sphal.link.default.shared_libs += libm.so
+namespace.sphal.link.default.shared_libs += libnativewindow.so
+namespace.sphal.link.default.shared_libs += libstdc++.so
+namespace.sphal.link.default.shared_libs += libsync.so
+namespace.sphal.link.default.shared_libs += libvndksupport.so
+namespace.sphal.link.default.shared_libs += libz.so
# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so
+namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
+namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
+namespace.sphal.link.vndk.shared_libs += android.hidl.memory@1.0.so
+namespace.sphal.link.vndk.shared_libs += libbase.so
+namespace.sphal.link.vndk.shared_libs += libc++.so
+namespace.sphal.link.vndk.shared_libs += libcutils.so
+namespace.sphal.link.vndk.shared_libs += libhardware.so
+namespace.sphal.link.vndk.shared_libs += libhidlbase.so
+namespace.sphal.link.vndk.shared_libs += libhidlmemory.so
+namespace.sphal.link.vndk.shared_libs += libhidltransport.so
+namespace.sphal.link.vndk.shared_libs += libhwbinder.so
+namespace.sphal.link.vndk.shared_libs += libion.so
+namespace.sphal.link.vndk.shared_libs += libutils.so
# Renderscript gets separate namespace
namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -91,15 +125,68 @@
###############################################################################
namespace.rs.isolated = true
namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
-namespace.rs.permitted.paths = /vendor/${LIB}:/data
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
+namespace.rs.search.paths = /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths += /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths += /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths += /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths += /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so:libz.so:libft2.so
-namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+
+namespace.rs.link.default.shared_libs = libc.so
+namespace.rs.link.default.shared_libs += libEGL.so
+namespace.rs.link.default.shared_libs += libGLESv1_CM.so
+namespace.rs.link.default.shared_libs += libGLESv2.so
+namespace.rs.link.default.shared_libs += libdl.so
+namespace.rs.link.default.shared_libs += liblog.so
+namespace.rs.link.default.shared_libs += libm.so
+namespace.rs.link.default.shared_libs += libnativewindow.so
+namespace.rs.link.default.shared_libs += libstdc++.so
+namespace.rs.link.default.shared_libs += libsync.so
+namespace.rs.link.default.shared_libs += libvndksupport.so
+namespace.rs.link.default.shared_libs += libz.so
+# These two libs are private LLNDK libs but are exceptionally visible
+# in this 'rs' namespace because RenderScript framework libraries
+# which are loaded into this namespace are using them.
+namespace.rs.link.default.shared_libs += libft2.so
+namespace.rs.link.default.shared_libs += libmediandk.so
+
+namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so
+namespace.rs.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
+namespace.rs.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
+namespace.rs.link.vndk.shared_libs += android.hidl.memory@1.0.so
+namespace.rs.link.vndk.shared_libs += libbase.so
+namespace.rs.link.vndk.shared_libs += libc++.so
+namespace.rs.link.vndk.shared_libs += libcutils.so
+namespace.rs.link.vndk.shared_libs += libhardware.so
+namespace.rs.link.vndk.shared_libs += libhidlbase.so
+namespace.rs.link.vndk.shared_libs += libhidlmemory.so
+namespace.rs.link.vndk.shared_libs += libhidltransport.so
+namespace.rs.link.vndk.shared_libs += libhwbinder.so
+namespace.rs.link.vndk.shared_libs += libion.so
+namespace.rs.link.vndk.shared_libs += libutils.so
###############################################################################
# "vndk" namespace
@@ -108,17 +195,47 @@
###############################################################################
namespace.vndk.isolated = true
namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
+namespace.vndk.search.paths = /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.permitted.paths = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+
+namespace.vndk.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.asan.permitted.paths = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /vendor/${LIB}/egl
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
# Android releases.
namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so:libz.so
+namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so
+namespace.vndk.link.default.shared_libs += libEGL.so
+namespace.vndk.link.default.shared_libs += libc.so
+namespace.vndk.link.default.shared_libs += libdl.so
+namespace.vndk.link.default.shared_libs += liblog.so
+namespace.vndk.link.default.shared_libs += libm.so
+namespace.vndk.link.default.shared_libs += libnativewindow.so
+namespace.vndk.link.default.shared_libs += libstdc++.so
+namespace.vndk.link.default.shared_libs += libsync.so
+namespace.vndk.link.default.shared_libs += libvndksupport.so
+namespace.vndk.link.default.shared_libs += libz.so
###############################################################################
# Namespace config for vendor processes. In O, no restriction is enforced for
@@ -128,6 +245,45 @@
###############################################################################
[vendor]
namespace.default.isolated = false
-namespace.default.search.paths = /odm/${LIB}/hw:/odm/${LIB}/egl:/odm/${LIB}:/vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk${VNDK_VER}:/odm/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}
-namespace.default.asan.search.paths = /data/asan/odm/${LIB}/hw:/odm/${LIB}/hw:/data/asan/odm/${LIB}/egl:/odm/${LIB}/egl:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/odm/${LIB}/vndk-sp${VNDK_VER}:/odm/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}:/system/${LIB}
+namespace.default.search.paths = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+
+# Access to system libraries are allowed
+namespace.default.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.search.paths += /system/${LIB}
+
+# TODO(b/70551668) Remove /vendor/${LIB}/hw from search paths.
+# Shared libraries in the directory should be dlopened with full file paths.
+# This is a workaround for some legacy prebuilt binaries.
+namespace.default.search.paths += /vendor/${LIB}/hw
+
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /odm/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+
+# TODO(b/70551668) Remove /vendor/${LIB}/hw from search paths.
+# Shared libraries in the directory should be dlopened with full file paths.
+# This is a workaround for some legacy prebuilt binaries.
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/hw
+namespace.default.asan.search.paths += /vendor/${LIB}/hw
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 2d05fca..0e43de7 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -7,11 +7,18 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+
+dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
dir.vendor = /data/nativetest/vendor
dir.vendor = /data/nativetest64/vendor
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
+
dir.system = /data/nativetest
dir.system = /data/nativetest64
dir.system = /data/benchmarktest
@@ -27,13 +34,45 @@
# can't be loaded in this namespace.
###############################################################################
namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
-# /vendor/app, /vendor/framework were added since libart should be able to dlopen
-# the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.search.paths = /system/${LIB}
+
+# We can't have entire /system/${LIB} as permitted paths because doing so
+# makes it possible to load libs in /system/${LIB}/vndk* directories by
+# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
+# VNDK libs are built with previous versions of Android and thus must not be
+# loaded into this namespace where libs built with the current version of
+# Android are loaded. Mixing the two types of libs in the same namespace can
+# cause unexpected problem.
+namespace.default.permitted.paths = /system/${LIB}/drm
+namespace.default.permitted.paths += /system/${LIB}/extractors
+namespace.default.permitted.paths += /system/${LIB}/hw
+# These are where odex files are located. libart has to be able to dlopen the files
+namespace.default.permitted.paths += /system/framework
+namespace.default.permitted.paths += /system/app
+namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /vendor/framework
+namespace.default.permitted.paths += /vendor/app
+namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /oem/app
+namespace.default.permitted.paths += /data
+namespace.default.permitted.paths += /mnt/expand
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+
+namespace.default.asan.permitted.paths = /data
+namespace.default.asan.permitted.paths += /system/${LIB}/drm
+namespace.default.asan.permitted.paths += /system/${LIB}/extractors
+namespace.default.asan.permitted.paths += /system/${LIB}/hw
+namespace.default.asan.permitted.paths += /system/framework
+namespace.default.asan.permitted.paths += /system/app
+namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /vendor/framework
+namespace.default.asan.permitted.paths += /vendor/app
+namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /mnt/expand
###############################################################################
# "sphal" namespace
@@ -50,20 +89,30 @@
###############################################################################
namespace.sphal.isolated = true
namespace.sphal.visible = true
-namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
-namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.sphal.search.paths = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths += /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths += /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths += /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
# libs listed here can be used.
namespace.sphal.links = default,vndk,rs
-# WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
# Renderscript gets separate namespace
@@ -79,17 +128,42 @@
###############################################################################
namespace.rs.isolated = true
namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
-namespace.rs.permitted.paths = /vendor/${LIB}:/data
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
+namespace.rs.search.paths = /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths += /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths += /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths += /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths += /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
# namespace because RS framework libs are using them.
namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
###############################################################################
@@ -99,17 +173,43 @@
###############################################################################
namespace.vndk.isolated = true
namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
+namespace.vndk.search.paths = /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.permitted.paths = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+
+namespace.vndk.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.asan.permitted.paths = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.vndk.asan.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
# Android releases.
namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
###############################################################################
# Namespace config for vendor processes. In O, no restriction is enforced for
@@ -133,13 +233,45 @@
namespace.default.isolated = true
namespace.default.visible = true
-namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}
-namespace.default.permitted.paths = /vendor
+namespace.default.search.paths = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor
+# TODO(b/70551668) remove this
+namespace.default.search.paths += /vendor/${LIB}/hw
+
+namespace.default.permitted.paths = /odm
+namespace.default.permitted.paths += /vendor
+
+namespace.default.asan.search.paths = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /odm/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /odm/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /vendor/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /vendor/${LIB}/vndk-sp${VNDK_VER}
+
+# TODO(b/70551668) remove this
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/hw
+namespace.default.asan.search.paths += /vendor/${LIB}/hw
+
+namespace.default.asan.permitted.paths = /data/asan/odm
+namespace.default.asan.permitted.paths += /odm
+namespace.default.asan.permitted.paths += /data/asan/vendor
+namespace.default.asan.permitted.paths += /vendor
namespace.default.links = system
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%:%VNDK_SAMEPROCESS_LIBRARIES%:%VNDK_CORE_LIBRARIES%
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.system.shared_libs += %VNDK_SAMEPROCESS_LIBRARIES%
+namespace.default.link.system.shared_libs += %VNDK_CORE_LIBRARIES%
###############################################################################
# "system" namespace
@@ -148,6 +280,14 @@
# a vendor process.
###############################################################################
namespace.system.isolated = false
-namespace.system.search.paths = /system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}
-namespace.system.asan.search.paths = /data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}:/system/${LIB}
+namespace.system.search.paths = /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.system.search.paths += /system/${LIB}
+
+namespace.system.asan.search.paths = /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
+namespace.system.asan.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.system.asan.search.paths += /data/asan/system/${LIB}
+namespace.system.asan.search.paths += /system/${LIB}